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_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6322    init_test(cx, |_| {});
 6323
 6324    let language = Arc::new(Language::new(
 6325        LanguageConfig::default(),
 6326        Some(tree_sitter_rust::LANGUAGE.into()),
 6327    ));
 6328
 6329    let text = r#"
 6330        use mod1::mod2::{mod3, mod4};
 6331
 6332        fn fn_1(param1: bool, param2: &str) {
 6333            let var1 = "hello world";
 6334        }
 6335    "#
 6336    .unindent();
 6337
 6338    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6339    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6340    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6341
 6342    editor
 6343        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6344        .await;
 6345
 6346    // Test 1: Cursor on a letter of a string word
 6347    editor.update_in(cx, |editor, window, cx| {
 6348        editor.change_selections(None, window, cx, |s| {
 6349            s.select_display_ranges([
 6350                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6351            ]);
 6352        });
 6353    });
 6354    editor.update_in(cx, |editor, window, cx| {
 6355        assert_text_with_selections(
 6356            editor,
 6357            indoc! {r#"
 6358                use mod1::mod2::{mod3, mod4};
 6359
 6360                fn fn_1(param1: bool, param2: &str) {
 6361                    let var1 = "hˇello world";
 6362                }
 6363            "#},
 6364            cx,
 6365        );
 6366        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6367        assert_text_with_selections(
 6368            editor,
 6369            indoc! {r#"
 6370                use mod1::mod2::{mod3, mod4};
 6371
 6372                fn fn_1(param1: bool, param2: &str) {
 6373                    let var1 = "«ˇhello» world";
 6374                }
 6375            "#},
 6376            cx,
 6377        );
 6378    });
 6379
 6380    // Test 2: Partial selection within a word
 6381    editor.update_in(cx, |editor, window, cx| {
 6382        editor.change_selections(None, window, cx, |s| {
 6383            s.select_display_ranges([
 6384                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6385            ]);
 6386        });
 6387    });
 6388    editor.update_in(cx, |editor, window, cx| {
 6389        assert_text_with_selections(
 6390            editor,
 6391            indoc! {r#"
 6392                use mod1::mod2::{mod3, mod4};
 6393
 6394                fn fn_1(param1: bool, param2: &str) {
 6395                    let var1 = "h«elˇ»lo world";
 6396                }
 6397            "#},
 6398            cx,
 6399        );
 6400        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6401        assert_text_with_selections(
 6402            editor,
 6403            indoc! {r#"
 6404                use mod1::mod2::{mod3, mod4};
 6405
 6406                fn fn_1(param1: bool, param2: &str) {
 6407                    let var1 = "«ˇhello» world";
 6408                }
 6409            "#},
 6410            cx,
 6411        );
 6412    });
 6413
 6414    // Test 3: Complete word already selected
 6415    editor.update_in(cx, |editor, window, cx| {
 6416        editor.change_selections(None, window, cx, |s| {
 6417            s.select_display_ranges([
 6418                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6419            ]);
 6420        });
 6421    });
 6422    editor.update_in(cx, |editor, window, cx| {
 6423        assert_text_with_selections(
 6424            editor,
 6425            indoc! {r#"
 6426                use mod1::mod2::{mod3, mod4};
 6427
 6428                fn fn_1(param1: bool, param2: &str) {
 6429                    let var1 = "«helloˇ» world";
 6430                }
 6431            "#},
 6432            cx,
 6433        );
 6434        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6435        assert_text_with_selections(
 6436            editor,
 6437            indoc! {r#"
 6438                use mod1::mod2::{mod3, mod4};
 6439
 6440                fn fn_1(param1: bool, param2: &str) {
 6441                    let var1 = "«hello worldˇ»";
 6442                }
 6443            "#},
 6444            cx,
 6445        );
 6446    });
 6447
 6448    // Test 4: Selection spanning across words
 6449    editor.update_in(cx, |editor, window, cx| {
 6450        editor.change_selections(None, window, cx, |s| {
 6451            s.select_display_ranges([
 6452                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6453            ]);
 6454        });
 6455    });
 6456    editor.update_in(cx, |editor, window, cx| {
 6457        assert_text_with_selections(
 6458            editor,
 6459            indoc! {r#"
 6460                use mod1::mod2::{mod3, mod4};
 6461
 6462                fn fn_1(param1: bool, param2: &str) {
 6463                    let var1 = "hel«lo woˇ»rld";
 6464                }
 6465            "#},
 6466            cx,
 6467        );
 6468        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6469        assert_text_with_selections(
 6470            editor,
 6471            indoc! {r#"
 6472                use mod1::mod2::{mod3, mod4};
 6473
 6474                fn fn_1(param1: bool, param2: &str) {
 6475                    let var1 = "«ˇhello world»";
 6476                }
 6477            "#},
 6478            cx,
 6479        );
 6480    });
 6481
 6482    // Test 5: Expansion beyond string
 6483    editor.update_in(cx, |editor, window, cx| {
 6484        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6485        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6486        assert_text_with_selections(
 6487            editor,
 6488            indoc! {r#"
 6489                use mod1::mod2::{mod3, mod4};
 6490
 6491                fn fn_1(param1: bool, param2: &str) {
 6492                    «ˇlet var1 = "hello world";»
 6493                }
 6494            "#},
 6495            cx,
 6496        );
 6497    });
 6498}
 6499
 6500#[gpui::test]
 6501async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6502    init_test(cx, |_| {});
 6503
 6504    let base_text = r#"
 6505        impl A {
 6506            // this is an uncommitted comment
 6507
 6508            fn b() {
 6509                c();
 6510            }
 6511
 6512            // this is another uncommitted comment
 6513
 6514            fn d() {
 6515                // e
 6516                // f
 6517            }
 6518        }
 6519
 6520        fn g() {
 6521            // h
 6522        }
 6523    "#
 6524    .unindent();
 6525
 6526    let text = r#"
 6527        ˇimpl A {
 6528
 6529            fn b() {
 6530                c();
 6531            }
 6532
 6533            fn d() {
 6534                // e
 6535                // f
 6536            }
 6537        }
 6538
 6539        fn g() {
 6540            // h
 6541        }
 6542    "#
 6543    .unindent();
 6544
 6545    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6546    cx.set_state(&text);
 6547    cx.set_head_text(&base_text);
 6548    cx.update_editor(|editor, window, cx| {
 6549        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6550    });
 6551
 6552    cx.assert_state_with_diff(
 6553        "
 6554        ˇimpl A {
 6555      -     // this is an uncommitted comment
 6556
 6557            fn b() {
 6558                c();
 6559            }
 6560
 6561      -     // this is another uncommitted comment
 6562      -
 6563            fn d() {
 6564                // e
 6565                // f
 6566            }
 6567        }
 6568
 6569        fn g() {
 6570            // h
 6571        }
 6572    "
 6573        .unindent(),
 6574    );
 6575
 6576    let expected_display_text = "
 6577        impl A {
 6578            // this is an uncommitted comment
 6579
 6580            fn b() {
 6581 6582            }
 6583
 6584            // this is another uncommitted comment
 6585
 6586            fn d() {
 6587 6588            }
 6589        }
 6590
 6591        fn g() {
 6592 6593        }
 6594        "
 6595    .unindent();
 6596
 6597    cx.update_editor(|editor, window, cx| {
 6598        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6599        assert_eq!(editor.display_text(cx), expected_display_text);
 6600    });
 6601}
 6602
 6603#[gpui::test]
 6604async fn test_autoindent(cx: &mut TestAppContext) {
 6605    init_test(cx, |_| {});
 6606
 6607    let language = Arc::new(
 6608        Language::new(
 6609            LanguageConfig {
 6610                brackets: BracketPairConfig {
 6611                    pairs: vec![
 6612                        BracketPair {
 6613                            start: "{".to_string(),
 6614                            end: "}".to_string(),
 6615                            close: false,
 6616                            surround: false,
 6617                            newline: true,
 6618                        },
 6619                        BracketPair {
 6620                            start: "(".to_string(),
 6621                            end: ")".to_string(),
 6622                            close: false,
 6623                            surround: false,
 6624                            newline: true,
 6625                        },
 6626                    ],
 6627                    ..Default::default()
 6628                },
 6629                ..Default::default()
 6630            },
 6631            Some(tree_sitter_rust::LANGUAGE.into()),
 6632        )
 6633        .with_indents_query(
 6634            r#"
 6635                (_ "(" ")" @end) @indent
 6636                (_ "{" "}" @end) @indent
 6637            "#,
 6638        )
 6639        .unwrap(),
 6640    );
 6641
 6642    let text = "fn a() {}";
 6643
 6644    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6645    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6646    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6647    editor
 6648        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6649        .await;
 6650
 6651    editor.update_in(cx, |editor, window, cx| {
 6652        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6653        editor.newline(&Newline, window, cx);
 6654        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6655        assert_eq!(
 6656            editor.selections.ranges(cx),
 6657            &[
 6658                Point::new(1, 4)..Point::new(1, 4),
 6659                Point::new(3, 4)..Point::new(3, 4),
 6660                Point::new(5, 0)..Point::new(5, 0)
 6661            ]
 6662        );
 6663    });
 6664}
 6665
 6666#[gpui::test]
 6667async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6668    init_test(cx, |_| {});
 6669
 6670    {
 6671        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6672        cx.set_state(indoc! {"
 6673            impl A {
 6674
 6675                fn b() {}
 6676
 6677            «fn c() {
 6678
 6679            }ˇ»
 6680            }
 6681        "});
 6682
 6683        cx.update_editor(|editor, window, cx| {
 6684            editor.autoindent(&Default::default(), window, cx);
 6685        });
 6686
 6687        cx.assert_editor_state(indoc! {"
 6688            impl A {
 6689
 6690                fn b() {}
 6691
 6692                «fn c() {
 6693
 6694                }ˇ»
 6695            }
 6696        "});
 6697    }
 6698
 6699    {
 6700        let mut cx = EditorTestContext::new_multibuffer(
 6701            cx,
 6702            [indoc! { "
 6703                impl A {
 6704                «
 6705                // a
 6706                fn b(){}
 6707                »
 6708                «
 6709                    }
 6710                    fn c(){}
 6711                »
 6712            "}],
 6713        );
 6714
 6715        let buffer = cx.update_editor(|editor, _, cx| {
 6716            let buffer = editor.buffer().update(cx, |buffer, _| {
 6717                buffer.all_buffers().iter().next().unwrap().clone()
 6718            });
 6719            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6720            buffer
 6721        });
 6722
 6723        cx.run_until_parked();
 6724        cx.update_editor(|editor, window, cx| {
 6725            editor.select_all(&Default::default(), window, cx);
 6726            editor.autoindent(&Default::default(), window, cx)
 6727        });
 6728        cx.run_until_parked();
 6729
 6730        cx.update(|_, cx| {
 6731            assert_eq!(
 6732                buffer.read(cx).text(),
 6733                indoc! { "
 6734                    impl A {
 6735
 6736                        // a
 6737                        fn b(){}
 6738
 6739
 6740                    }
 6741                    fn c(){}
 6742
 6743                " }
 6744            )
 6745        });
 6746    }
 6747}
 6748
 6749#[gpui::test]
 6750async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6751    init_test(cx, |_| {});
 6752
 6753    let mut cx = EditorTestContext::new(cx).await;
 6754
 6755    let language = Arc::new(Language::new(
 6756        LanguageConfig {
 6757            brackets: BracketPairConfig {
 6758                pairs: vec![
 6759                    BracketPair {
 6760                        start: "{".to_string(),
 6761                        end: "}".to_string(),
 6762                        close: true,
 6763                        surround: true,
 6764                        newline: true,
 6765                    },
 6766                    BracketPair {
 6767                        start: "(".to_string(),
 6768                        end: ")".to_string(),
 6769                        close: true,
 6770                        surround: true,
 6771                        newline: true,
 6772                    },
 6773                    BracketPair {
 6774                        start: "/*".to_string(),
 6775                        end: " */".to_string(),
 6776                        close: true,
 6777                        surround: true,
 6778                        newline: true,
 6779                    },
 6780                    BracketPair {
 6781                        start: "[".to_string(),
 6782                        end: "]".to_string(),
 6783                        close: false,
 6784                        surround: false,
 6785                        newline: true,
 6786                    },
 6787                    BracketPair {
 6788                        start: "\"".to_string(),
 6789                        end: "\"".to_string(),
 6790                        close: true,
 6791                        surround: true,
 6792                        newline: false,
 6793                    },
 6794                    BracketPair {
 6795                        start: "<".to_string(),
 6796                        end: ">".to_string(),
 6797                        close: false,
 6798                        surround: true,
 6799                        newline: true,
 6800                    },
 6801                ],
 6802                ..Default::default()
 6803            },
 6804            autoclose_before: "})]".to_string(),
 6805            ..Default::default()
 6806        },
 6807        Some(tree_sitter_rust::LANGUAGE.into()),
 6808    ));
 6809
 6810    cx.language_registry().add(language.clone());
 6811    cx.update_buffer(|buffer, cx| {
 6812        buffer.set_language(Some(language), cx);
 6813    });
 6814
 6815    cx.set_state(
 6816        &r#"
 6817            🏀ˇ
 6818            εˇ
 6819            ❤️ˇ
 6820        "#
 6821        .unindent(),
 6822    );
 6823
 6824    // autoclose multiple nested brackets at multiple cursors
 6825    cx.update_editor(|editor, window, cx| {
 6826        editor.handle_input("{", window, cx);
 6827        editor.handle_input("{", window, cx);
 6828        editor.handle_input("{", window, cx);
 6829    });
 6830    cx.assert_editor_state(
 6831        &"
 6832            🏀{{{ˇ}}}
 6833            ε{{{ˇ}}}
 6834            ❤️{{{ˇ}}}
 6835        "
 6836        .unindent(),
 6837    );
 6838
 6839    // insert a different closing bracket
 6840    cx.update_editor(|editor, window, cx| {
 6841        editor.handle_input(")", window, cx);
 6842    });
 6843    cx.assert_editor_state(
 6844        &"
 6845            🏀{{{)ˇ}}}
 6846            ε{{{)ˇ}}}
 6847            ❤️{{{)ˇ}}}
 6848        "
 6849        .unindent(),
 6850    );
 6851
 6852    // skip over the auto-closed brackets when typing a closing bracket
 6853    cx.update_editor(|editor, window, cx| {
 6854        editor.move_right(&MoveRight, window, cx);
 6855        editor.handle_input("}", window, cx);
 6856        editor.handle_input("}", window, cx);
 6857        editor.handle_input("}", window, cx);
 6858    });
 6859    cx.assert_editor_state(
 6860        &"
 6861            🏀{{{)}}}}ˇ
 6862            ε{{{)}}}}ˇ
 6863            ❤️{{{)}}}}ˇ
 6864        "
 6865        .unindent(),
 6866    );
 6867
 6868    // autoclose multi-character pairs
 6869    cx.set_state(
 6870        &"
 6871            ˇ
 6872            ˇ
 6873        "
 6874        .unindent(),
 6875    );
 6876    cx.update_editor(|editor, window, cx| {
 6877        editor.handle_input("/", window, cx);
 6878        editor.handle_input("*", window, cx);
 6879    });
 6880    cx.assert_editor_state(
 6881        &"
 6882            /*ˇ */
 6883            /*ˇ */
 6884        "
 6885        .unindent(),
 6886    );
 6887
 6888    // one cursor autocloses a multi-character pair, one cursor
 6889    // does not autoclose.
 6890    cx.set_state(
 6891        &"
 6892 6893            ˇ
 6894        "
 6895        .unindent(),
 6896    );
 6897    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6898    cx.assert_editor_state(
 6899        &"
 6900            /*ˇ */
 6901 6902        "
 6903        .unindent(),
 6904    );
 6905
 6906    // Don't autoclose if the next character isn't whitespace and isn't
 6907    // listed in the language's "autoclose_before" section.
 6908    cx.set_state("ˇa b");
 6909    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6910    cx.assert_editor_state("{ˇa b");
 6911
 6912    // Don't autoclose if `close` is false for the bracket pair
 6913    cx.set_state("ˇ");
 6914    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6915    cx.assert_editor_state("");
 6916
 6917    // Surround with brackets if text is selected
 6918    cx.set_state("«aˇ» b");
 6919    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6920    cx.assert_editor_state("{«aˇ»} b");
 6921
 6922    // Autoclose when not immediately after a word character
 6923    cx.set_state("a ˇ");
 6924    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6925    cx.assert_editor_state("a \"ˇ\"");
 6926
 6927    // Autoclose pair where the start and end characters are the same
 6928    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6929    cx.assert_editor_state("a \"\"ˇ");
 6930
 6931    // Don't autoclose when immediately after a word character
 6932    cx.set_state("");
 6933    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6934    cx.assert_editor_state("a\"ˇ");
 6935
 6936    // Do autoclose when after a non-word character
 6937    cx.set_state("");
 6938    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6939    cx.assert_editor_state("{\"ˇ\"");
 6940
 6941    // Non identical pairs autoclose regardless of preceding character
 6942    cx.set_state("");
 6943    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6944    cx.assert_editor_state("a{ˇ}");
 6945
 6946    // Don't autoclose pair if autoclose is disabled
 6947    cx.set_state("ˇ");
 6948    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6949    cx.assert_editor_state("");
 6950
 6951    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6952    cx.set_state("«aˇ» b");
 6953    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6954    cx.assert_editor_state("<«aˇ»> b");
 6955}
 6956
 6957#[gpui::test]
 6958async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6959    init_test(cx, |settings| {
 6960        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6961    });
 6962
 6963    let mut cx = EditorTestContext::new(cx).await;
 6964
 6965    let language = Arc::new(Language::new(
 6966        LanguageConfig {
 6967            brackets: BracketPairConfig {
 6968                pairs: vec![
 6969                    BracketPair {
 6970                        start: "{".to_string(),
 6971                        end: "}".to_string(),
 6972                        close: true,
 6973                        surround: true,
 6974                        newline: true,
 6975                    },
 6976                    BracketPair {
 6977                        start: "(".to_string(),
 6978                        end: ")".to_string(),
 6979                        close: true,
 6980                        surround: true,
 6981                        newline: true,
 6982                    },
 6983                    BracketPair {
 6984                        start: "[".to_string(),
 6985                        end: "]".to_string(),
 6986                        close: false,
 6987                        surround: false,
 6988                        newline: true,
 6989                    },
 6990                ],
 6991                ..Default::default()
 6992            },
 6993            autoclose_before: "})]".to_string(),
 6994            ..Default::default()
 6995        },
 6996        Some(tree_sitter_rust::LANGUAGE.into()),
 6997    ));
 6998
 6999    cx.language_registry().add(language.clone());
 7000    cx.update_buffer(|buffer, cx| {
 7001        buffer.set_language(Some(language), cx);
 7002    });
 7003
 7004    cx.set_state(
 7005        &"
 7006            ˇ
 7007            ˇ
 7008            ˇ
 7009        "
 7010        .unindent(),
 7011    );
 7012
 7013    // ensure only matching closing brackets are skipped over
 7014    cx.update_editor(|editor, window, cx| {
 7015        editor.handle_input("}", window, cx);
 7016        editor.move_left(&MoveLeft, window, cx);
 7017        editor.handle_input(")", window, cx);
 7018        editor.move_left(&MoveLeft, window, cx);
 7019    });
 7020    cx.assert_editor_state(
 7021        &"
 7022            ˇ)}
 7023            ˇ)}
 7024            ˇ)}
 7025        "
 7026        .unindent(),
 7027    );
 7028
 7029    // skip-over closing brackets at multiple cursors
 7030    cx.update_editor(|editor, window, cx| {
 7031        editor.handle_input(")", window, cx);
 7032        editor.handle_input("}", window, cx);
 7033    });
 7034    cx.assert_editor_state(
 7035        &"
 7036            )}ˇ
 7037            )}ˇ
 7038            )}ˇ
 7039        "
 7040        .unindent(),
 7041    );
 7042
 7043    // ignore non-close brackets
 7044    cx.update_editor(|editor, window, cx| {
 7045        editor.handle_input("]", window, cx);
 7046        editor.move_left(&MoveLeft, window, cx);
 7047        editor.handle_input("]", window, cx);
 7048    });
 7049    cx.assert_editor_state(
 7050        &"
 7051            )}]ˇ]
 7052            )}]ˇ]
 7053            )}]ˇ]
 7054        "
 7055        .unindent(),
 7056    );
 7057}
 7058
 7059#[gpui::test]
 7060async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7061    init_test(cx, |_| {});
 7062
 7063    let mut cx = EditorTestContext::new(cx).await;
 7064
 7065    let html_language = Arc::new(
 7066        Language::new(
 7067            LanguageConfig {
 7068                name: "HTML".into(),
 7069                brackets: BracketPairConfig {
 7070                    pairs: vec![
 7071                        BracketPair {
 7072                            start: "<".into(),
 7073                            end: ">".into(),
 7074                            close: true,
 7075                            ..Default::default()
 7076                        },
 7077                        BracketPair {
 7078                            start: "{".into(),
 7079                            end: "}".into(),
 7080                            close: true,
 7081                            ..Default::default()
 7082                        },
 7083                        BracketPair {
 7084                            start: "(".into(),
 7085                            end: ")".into(),
 7086                            close: true,
 7087                            ..Default::default()
 7088                        },
 7089                    ],
 7090                    ..Default::default()
 7091                },
 7092                autoclose_before: "})]>".into(),
 7093                ..Default::default()
 7094            },
 7095            Some(tree_sitter_html::LANGUAGE.into()),
 7096        )
 7097        .with_injection_query(
 7098            r#"
 7099            (script_element
 7100                (raw_text) @injection.content
 7101                (#set! injection.language "javascript"))
 7102            "#,
 7103        )
 7104        .unwrap(),
 7105    );
 7106
 7107    let javascript_language = Arc::new(Language::new(
 7108        LanguageConfig {
 7109            name: "JavaScript".into(),
 7110            brackets: BracketPairConfig {
 7111                pairs: vec![
 7112                    BracketPair {
 7113                        start: "/*".into(),
 7114                        end: " */".into(),
 7115                        close: true,
 7116                        ..Default::default()
 7117                    },
 7118                    BracketPair {
 7119                        start: "{".into(),
 7120                        end: "}".into(),
 7121                        close: true,
 7122                        ..Default::default()
 7123                    },
 7124                    BracketPair {
 7125                        start: "(".into(),
 7126                        end: ")".into(),
 7127                        close: true,
 7128                        ..Default::default()
 7129                    },
 7130                ],
 7131                ..Default::default()
 7132            },
 7133            autoclose_before: "})]>".into(),
 7134            ..Default::default()
 7135        },
 7136        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7137    ));
 7138
 7139    cx.language_registry().add(html_language.clone());
 7140    cx.language_registry().add(javascript_language.clone());
 7141
 7142    cx.update_buffer(|buffer, cx| {
 7143        buffer.set_language(Some(html_language), cx);
 7144    });
 7145
 7146    cx.set_state(
 7147        &r#"
 7148            <body>ˇ
 7149                <script>
 7150                    var x = 1;ˇ
 7151                </script>
 7152            </body>ˇ
 7153        "#
 7154        .unindent(),
 7155    );
 7156
 7157    // Precondition: different languages are active at different locations.
 7158    cx.update_editor(|editor, window, cx| {
 7159        let snapshot = editor.snapshot(window, cx);
 7160        let cursors = editor.selections.ranges::<usize>(cx);
 7161        let languages = cursors
 7162            .iter()
 7163            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7164            .collect::<Vec<_>>();
 7165        assert_eq!(
 7166            languages,
 7167            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7168        );
 7169    });
 7170
 7171    // Angle brackets autoclose in HTML, but not JavaScript.
 7172    cx.update_editor(|editor, window, cx| {
 7173        editor.handle_input("<", window, cx);
 7174        editor.handle_input("a", window, cx);
 7175    });
 7176    cx.assert_editor_state(
 7177        &r#"
 7178            <body><aˇ>
 7179                <script>
 7180                    var x = 1;<aˇ
 7181                </script>
 7182            </body><aˇ>
 7183        "#
 7184        .unindent(),
 7185    );
 7186
 7187    // Curly braces and parens autoclose in both HTML and JavaScript.
 7188    cx.update_editor(|editor, window, cx| {
 7189        editor.handle_input(" b=", window, cx);
 7190        editor.handle_input("{", window, cx);
 7191        editor.handle_input("c", window, cx);
 7192        editor.handle_input("(", window, cx);
 7193    });
 7194    cx.assert_editor_state(
 7195        &r#"
 7196            <body><a b={c(ˇ)}>
 7197                <script>
 7198                    var x = 1;<a b={c(ˇ)}
 7199                </script>
 7200            </body><a b={c(ˇ)}>
 7201        "#
 7202        .unindent(),
 7203    );
 7204
 7205    // Brackets that were already autoclosed are skipped.
 7206    cx.update_editor(|editor, window, cx| {
 7207        editor.handle_input(")", window, cx);
 7208        editor.handle_input("d", window, cx);
 7209        editor.handle_input("}", window, cx);
 7210    });
 7211    cx.assert_editor_state(
 7212        &r#"
 7213            <body><a b={c()d}ˇ>
 7214                <script>
 7215                    var x = 1;<a b={c()d}ˇ
 7216                </script>
 7217            </body><a b={c()d}ˇ>
 7218        "#
 7219        .unindent(),
 7220    );
 7221    cx.update_editor(|editor, window, cx| {
 7222        editor.handle_input(">", window, cx);
 7223    });
 7224    cx.assert_editor_state(
 7225        &r#"
 7226            <body><a b={c()d}>ˇ
 7227                <script>
 7228                    var x = 1;<a b={c()d}>ˇ
 7229                </script>
 7230            </body><a b={c()d}>ˇ
 7231        "#
 7232        .unindent(),
 7233    );
 7234
 7235    // Reset
 7236    cx.set_state(
 7237        &r#"
 7238            <body>ˇ
 7239                <script>
 7240                    var x = 1;ˇ
 7241                </script>
 7242            </body>ˇ
 7243        "#
 7244        .unindent(),
 7245    );
 7246
 7247    cx.update_editor(|editor, window, cx| {
 7248        editor.handle_input("<", window, cx);
 7249    });
 7250    cx.assert_editor_state(
 7251        &r#"
 7252            <body><ˇ>
 7253                <script>
 7254                    var x = 1;<ˇ
 7255                </script>
 7256            </body><ˇ>
 7257        "#
 7258        .unindent(),
 7259    );
 7260
 7261    // When backspacing, the closing angle brackets are removed.
 7262    cx.update_editor(|editor, window, cx| {
 7263        editor.backspace(&Backspace, window, cx);
 7264    });
 7265    cx.assert_editor_state(
 7266        &r#"
 7267            <body>ˇ
 7268                <script>
 7269                    var x = 1;ˇ
 7270                </script>
 7271            </body>ˇ
 7272        "#
 7273        .unindent(),
 7274    );
 7275
 7276    // Block comments autoclose in JavaScript, but not HTML.
 7277    cx.update_editor(|editor, window, cx| {
 7278        editor.handle_input("/", window, cx);
 7279        editor.handle_input("*", window, cx);
 7280    });
 7281    cx.assert_editor_state(
 7282        &r#"
 7283            <body>/*ˇ
 7284                <script>
 7285                    var x = 1;/*ˇ */
 7286                </script>
 7287            </body>/*ˇ
 7288        "#
 7289        .unindent(),
 7290    );
 7291}
 7292
 7293#[gpui::test]
 7294async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7295    init_test(cx, |_| {});
 7296
 7297    let mut cx = EditorTestContext::new(cx).await;
 7298
 7299    let rust_language = Arc::new(
 7300        Language::new(
 7301            LanguageConfig {
 7302                name: "Rust".into(),
 7303                brackets: serde_json::from_value(json!([
 7304                    { "start": "{", "end": "}", "close": true, "newline": true },
 7305                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7306                ]))
 7307                .unwrap(),
 7308                autoclose_before: "})]>".into(),
 7309                ..Default::default()
 7310            },
 7311            Some(tree_sitter_rust::LANGUAGE.into()),
 7312        )
 7313        .with_override_query("(string_literal) @string")
 7314        .unwrap(),
 7315    );
 7316
 7317    cx.language_registry().add(rust_language.clone());
 7318    cx.update_buffer(|buffer, cx| {
 7319        buffer.set_language(Some(rust_language), cx);
 7320    });
 7321
 7322    cx.set_state(
 7323        &r#"
 7324            let x = ˇ
 7325        "#
 7326        .unindent(),
 7327    );
 7328
 7329    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7330    cx.update_editor(|editor, window, cx| {
 7331        editor.handle_input("\"", window, cx);
 7332    });
 7333    cx.assert_editor_state(
 7334        &r#"
 7335            let x = "ˇ"
 7336        "#
 7337        .unindent(),
 7338    );
 7339
 7340    // Inserting another quotation mark. The cursor moves across the existing
 7341    // automatically-inserted quotation mark.
 7342    cx.update_editor(|editor, window, cx| {
 7343        editor.handle_input("\"", window, cx);
 7344    });
 7345    cx.assert_editor_state(
 7346        &r#"
 7347            let x = ""ˇ
 7348        "#
 7349        .unindent(),
 7350    );
 7351
 7352    // Reset
 7353    cx.set_state(
 7354        &r#"
 7355            let x = ˇ
 7356        "#
 7357        .unindent(),
 7358    );
 7359
 7360    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7361    cx.update_editor(|editor, window, cx| {
 7362        editor.handle_input("\"", window, cx);
 7363        editor.handle_input(" ", window, cx);
 7364        editor.move_left(&Default::default(), window, cx);
 7365        editor.handle_input("\\", window, cx);
 7366        editor.handle_input("\"", window, cx);
 7367    });
 7368    cx.assert_editor_state(
 7369        &r#"
 7370            let x = "\"ˇ "
 7371        "#
 7372        .unindent(),
 7373    );
 7374
 7375    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7376    // mark. Nothing is inserted.
 7377    cx.update_editor(|editor, window, cx| {
 7378        editor.move_right(&Default::default(), window, cx);
 7379        editor.handle_input("\"", window, cx);
 7380    });
 7381    cx.assert_editor_state(
 7382        &r#"
 7383            let x = "\" "ˇ
 7384        "#
 7385        .unindent(),
 7386    );
 7387}
 7388
 7389#[gpui::test]
 7390async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7391    init_test(cx, |_| {});
 7392
 7393    let language = Arc::new(Language::new(
 7394        LanguageConfig {
 7395            brackets: BracketPairConfig {
 7396                pairs: vec![
 7397                    BracketPair {
 7398                        start: "{".to_string(),
 7399                        end: "}".to_string(),
 7400                        close: true,
 7401                        surround: true,
 7402                        newline: true,
 7403                    },
 7404                    BracketPair {
 7405                        start: "/* ".to_string(),
 7406                        end: "*/".to_string(),
 7407                        close: true,
 7408                        surround: true,
 7409                        ..Default::default()
 7410                    },
 7411                ],
 7412                ..Default::default()
 7413            },
 7414            ..Default::default()
 7415        },
 7416        Some(tree_sitter_rust::LANGUAGE.into()),
 7417    ));
 7418
 7419    let text = r#"
 7420        a
 7421        b
 7422        c
 7423    "#
 7424    .unindent();
 7425
 7426    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7427    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7428    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7429    editor
 7430        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7431        .await;
 7432
 7433    editor.update_in(cx, |editor, window, cx| {
 7434        editor.change_selections(None, window, cx, |s| {
 7435            s.select_display_ranges([
 7436                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7437                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7438                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7439            ])
 7440        });
 7441
 7442        editor.handle_input("{", window, cx);
 7443        editor.handle_input("{", window, cx);
 7444        editor.handle_input("{", window, cx);
 7445        assert_eq!(
 7446            editor.text(cx),
 7447            "
 7448                {{{a}}}
 7449                {{{b}}}
 7450                {{{c}}}
 7451            "
 7452            .unindent()
 7453        );
 7454        assert_eq!(
 7455            editor.selections.display_ranges(cx),
 7456            [
 7457                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7458                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7459                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7460            ]
 7461        );
 7462
 7463        editor.undo(&Undo, window, cx);
 7464        editor.undo(&Undo, window, cx);
 7465        editor.undo(&Undo, window, cx);
 7466        assert_eq!(
 7467            editor.text(cx),
 7468            "
 7469                a
 7470                b
 7471                c
 7472            "
 7473            .unindent()
 7474        );
 7475        assert_eq!(
 7476            editor.selections.display_ranges(cx),
 7477            [
 7478                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7479                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7480                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7481            ]
 7482        );
 7483
 7484        // Ensure inserting the first character of a multi-byte bracket pair
 7485        // doesn't surround the selections with the bracket.
 7486        editor.handle_input("/", window, cx);
 7487        assert_eq!(
 7488            editor.text(cx),
 7489            "
 7490                /
 7491                /
 7492                /
 7493            "
 7494            .unindent()
 7495        );
 7496        assert_eq!(
 7497            editor.selections.display_ranges(cx),
 7498            [
 7499                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7500                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7501                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7502            ]
 7503        );
 7504
 7505        editor.undo(&Undo, window, cx);
 7506        assert_eq!(
 7507            editor.text(cx),
 7508            "
 7509                a
 7510                b
 7511                c
 7512            "
 7513            .unindent()
 7514        );
 7515        assert_eq!(
 7516            editor.selections.display_ranges(cx),
 7517            [
 7518                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7519                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7520                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7521            ]
 7522        );
 7523
 7524        // Ensure inserting the last character of a multi-byte bracket pair
 7525        // doesn't surround the selections with the bracket.
 7526        editor.handle_input("*", window, cx);
 7527        assert_eq!(
 7528            editor.text(cx),
 7529            "
 7530                *
 7531                *
 7532                *
 7533            "
 7534            .unindent()
 7535        );
 7536        assert_eq!(
 7537            editor.selections.display_ranges(cx),
 7538            [
 7539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7540                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7541                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7542            ]
 7543        );
 7544    });
 7545}
 7546
 7547#[gpui::test]
 7548async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7549    init_test(cx, |_| {});
 7550
 7551    let language = Arc::new(Language::new(
 7552        LanguageConfig {
 7553            brackets: BracketPairConfig {
 7554                pairs: vec![BracketPair {
 7555                    start: "{".to_string(),
 7556                    end: "}".to_string(),
 7557                    close: true,
 7558                    surround: true,
 7559                    newline: true,
 7560                }],
 7561                ..Default::default()
 7562            },
 7563            autoclose_before: "}".to_string(),
 7564            ..Default::default()
 7565        },
 7566        Some(tree_sitter_rust::LANGUAGE.into()),
 7567    ));
 7568
 7569    let text = r#"
 7570        a
 7571        b
 7572        c
 7573    "#
 7574    .unindent();
 7575
 7576    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7577    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7578    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7579    editor
 7580        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7581        .await;
 7582
 7583    editor.update_in(cx, |editor, window, cx| {
 7584        editor.change_selections(None, window, cx, |s| {
 7585            s.select_ranges([
 7586                Point::new(0, 1)..Point::new(0, 1),
 7587                Point::new(1, 1)..Point::new(1, 1),
 7588                Point::new(2, 1)..Point::new(2, 1),
 7589            ])
 7590        });
 7591
 7592        editor.handle_input("{", window, cx);
 7593        editor.handle_input("{", window, cx);
 7594        editor.handle_input("_", window, cx);
 7595        assert_eq!(
 7596            editor.text(cx),
 7597            "
 7598                a{{_}}
 7599                b{{_}}
 7600                c{{_}}
 7601            "
 7602            .unindent()
 7603        );
 7604        assert_eq!(
 7605            editor.selections.ranges::<Point>(cx),
 7606            [
 7607                Point::new(0, 4)..Point::new(0, 4),
 7608                Point::new(1, 4)..Point::new(1, 4),
 7609                Point::new(2, 4)..Point::new(2, 4)
 7610            ]
 7611        );
 7612
 7613        editor.backspace(&Default::default(), window, cx);
 7614        editor.backspace(&Default::default(), window, cx);
 7615        assert_eq!(
 7616            editor.text(cx),
 7617            "
 7618                a{}
 7619                b{}
 7620                c{}
 7621            "
 7622            .unindent()
 7623        );
 7624        assert_eq!(
 7625            editor.selections.ranges::<Point>(cx),
 7626            [
 7627                Point::new(0, 2)..Point::new(0, 2),
 7628                Point::new(1, 2)..Point::new(1, 2),
 7629                Point::new(2, 2)..Point::new(2, 2)
 7630            ]
 7631        );
 7632
 7633        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7634        assert_eq!(
 7635            editor.text(cx),
 7636            "
 7637                a
 7638                b
 7639                c
 7640            "
 7641            .unindent()
 7642        );
 7643        assert_eq!(
 7644            editor.selections.ranges::<Point>(cx),
 7645            [
 7646                Point::new(0, 1)..Point::new(0, 1),
 7647                Point::new(1, 1)..Point::new(1, 1),
 7648                Point::new(2, 1)..Point::new(2, 1)
 7649            ]
 7650        );
 7651    });
 7652}
 7653
 7654#[gpui::test]
 7655async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7656    init_test(cx, |settings| {
 7657        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7658    });
 7659
 7660    let mut cx = EditorTestContext::new(cx).await;
 7661
 7662    let language = Arc::new(Language::new(
 7663        LanguageConfig {
 7664            brackets: BracketPairConfig {
 7665                pairs: vec![
 7666                    BracketPair {
 7667                        start: "{".to_string(),
 7668                        end: "}".to_string(),
 7669                        close: true,
 7670                        surround: true,
 7671                        newline: true,
 7672                    },
 7673                    BracketPair {
 7674                        start: "(".to_string(),
 7675                        end: ")".to_string(),
 7676                        close: true,
 7677                        surround: true,
 7678                        newline: true,
 7679                    },
 7680                    BracketPair {
 7681                        start: "[".to_string(),
 7682                        end: "]".to_string(),
 7683                        close: false,
 7684                        surround: true,
 7685                        newline: true,
 7686                    },
 7687                ],
 7688                ..Default::default()
 7689            },
 7690            autoclose_before: "})]".to_string(),
 7691            ..Default::default()
 7692        },
 7693        Some(tree_sitter_rust::LANGUAGE.into()),
 7694    ));
 7695
 7696    cx.language_registry().add(language.clone());
 7697    cx.update_buffer(|buffer, cx| {
 7698        buffer.set_language(Some(language), cx);
 7699    });
 7700
 7701    cx.set_state(
 7702        &"
 7703            {(ˇ)}
 7704            [[ˇ]]
 7705            {(ˇ)}
 7706        "
 7707        .unindent(),
 7708    );
 7709
 7710    cx.update_editor(|editor, window, cx| {
 7711        editor.backspace(&Default::default(), window, cx);
 7712        editor.backspace(&Default::default(), window, cx);
 7713    });
 7714
 7715    cx.assert_editor_state(
 7716        &"
 7717            ˇ
 7718            ˇ]]
 7719            ˇ
 7720        "
 7721        .unindent(),
 7722    );
 7723
 7724    cx.update_editor(|editor, window, cx| {
 7725        editor.handle_input("{", window, cx);
 7726        editor.handle_input("{", window, cx);
 7727        editor.move_right(&MoveRight, window, cx);
 7728        editor.move_right(&MoveRight, window, cx);
 7729        editor.move_left(&MoveLeft, window, cx);
 7730        editor.move_left(&MoveLeft, window, cx);
 7731        editor.backspace(&Default::default(), window, cx);
 7732    });
 7733
 7734    cx.assert_editor_state(
 7735        &"
 7736            {ˇ}
 7737            {ˇ}]]
 7738            {ˇ}
 7739        "
 7740        .unindent(),
 7741    );
 7742
 7743    cx.update_editor(|editor, window, cx| {
 7744        editor.backspace(&Default::default(), window, cx);
 7745    });
 7746
 7747    cx.assert_editor_state(
 7748        &"
 7749            ˇ
 7750            ˇ]]
 7751            ˇ
 7752        "
 7753        .unindent(),
 7754    );
 7755}
 7756
 7757#[gpui::test]
 7758async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7759    init_test(cx, |_| {});
 7760
 7761    let language = Arc::new(Language::new(
 7762        LanguageConfig::default(),
 7763        Some(tree_sitter_rust::LANGUAGE.into()),
 7764    ));
 7765
 7766    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7767    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7768    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7769    editor
 7770        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7771        .await;
 7772
 7773    editor.update_in(cx, |editor, window, cx| {
 7774        editor.set_auto_replace_emoji_shortcode(true);
 7775
 7776        editor.handle_input("Hello ", window, cx);
 7777        editor.handle_input(":wave", window, cx);
 7778        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7779
 7780        editor.handle_input(":", window, cx);
 7781        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7782
 7783        editor.handle_input(" :smile", window, cx);
 7784        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7785
 7786        editor.handle_input(":", window, cx);
 7787        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7788
 7789        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7790        editor.handle_input(":wave", window, cx);
 7791        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7792
 7793        editor.handle_input(":", window, cx);
 7794        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7795
 7796        editor.handle_input(":1", window, cx);
 7797        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7798
 7799        editor.handle_input(":", window, cx);
 7800        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7801
 7802        // Ensure shortcode does not get replaced when it is part of a word
 7803        editor.handle_input(" Test:wave", window, cx);
 7804        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7805
 7806        editor.handle_input(":", window, cx);
 7807        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7808
 7809        editor.set_auto_replace_emoji_shortcode(false);
 7810
 7811        // Ensure shortcode does not get replaced when auto replace is off
 7812        editor.handle_input(" :wave", window, cx);
 7813        assert_eq!(
 7814            editor.text(cx),
 7815            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7816        );
 7817
 7818        editor.handle_input(":", window, cx);
 7819        assert_eq!(
 7820            editor.text(cx),
 7821            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7822        );
 7823    });
 7824}
 7825
 7826#[gpui::test]
 7827async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7828    init_test(cx, |_| {});
 7829
 7830    let (text, insertion_ranges) = marked_text_ranges(
 7831        indoc! {"
 7832            ˇ
 7833        "},
 7834        false,
 7835    );
 7836
 7837    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7838    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7839
 7840    _ = editor.update_in(cx, |editor, window, cx| {
 7841        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7842
 7843        editor
 7844            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7845            .unwrap();
 7846
 7847        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7848            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7849            assert_eq!(editor.text(cx), expected_text);
 7850            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7851        }
 7852
 7853        assert(
 7854            editor,
 7855            cx,
 7856            indoc! {"
 7857            type «» =•
 7858            "},
 7859        );
 7860
 7861        assert!(editor.context_menu_visible(), "There should be a matches");
 7862    });
 7863}
 7864
 7865#[gpui::test]
 7866async fn test_snippets(cx: &mut TestAppContext) {
 7867    init_test(cx, |_| {});
 7868
 7869    let (text, insertion_ranges) = marked_text_ranges(
 7870        indoc! {"
 7871            a.ˇ b
 7872            a.ˇ b
 7873            a.ˇ b
 7874        "},
 7875        false,
 7876    );
 7877
 7878    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7879    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7880
 7881    editor.update_in(cx, |editor, window, cx| {
 7882        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7883
 7884        editor
 7885            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7886            .unwrap();
 7887
 7888        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7889            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7890            assert_eq!(editor.text(cx), expected_text);
 7891            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7892        }
 7893
 7894        assert(
 7895            editor,
 7896            cx,
 7897            indoc! {"
 7898                a.f(«one», two, «three») b
 7899                a.f(«one», two, «three») b
 7900                a.f(«one», two, «three») b
 7901            "},
 7902        );
 7903
 7904        // Can't move earlier than the first tab stop
 7905        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7906        assert(
 7907            editor,
 7908            cx,
 7909            indoc! {"
 7910                a.f(«one», two, «three») b
 7911                a.f(«one», two, «three») b
 7912                a.f(«one», two, «three») b
 7913            "},
 7914        );
 7915
 7916        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7917        assert(
 7918            editor,
 7919            cx,
 7920            indoc! {"
 7921                a.f(one, «two», three) b
 7922                a.f(one, «two», three) b
 7923                a.f(one, «two», three) b
 7924            "},
 7925        );
 7926
 7927        editor.move_to_prev_snippet_tabstop(window, cx);
 7928        assert(
 7929            editor,
 7930            cx,
 7931            indoc! {"
 7932                a.f(«one», two, «three») b
 7933                a.f(«one», two, «three») b
 7934                a.f(«one», two, «three») b
 7935            "},
 7936        );
 7937
 7938        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7939        assert(
 7940            editor,
 7941            cx,
 7942            indoc! {"
 7943                a.f(one, «two», three) b
 7944                a.f(one, «two», three) b
 7945                a.f(one, «two», three) b
 7946            "},
 7947        );
 7948        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7949        assert(
 7950            editor,
 7951            cx,
 7952            indoc! {"
 7953                a.f(one, two, three)ˇ b
 7954                a.f(one, two, three)ˇ b
 7955                a.f(one, two, three)ˇ b
 7956            "},
 7957        );
 7958
 7959        // As soon as the last tab stop is reached, snippet state is gone
 7960        editor.move_to_prev_snippet_tabstop(window, cx);
 7961        assert(
 7962            editor,
 7963            cx,
 7964            indoc! {"
 7965                a.f(one, two, three)ˇ b
 7966                a.f(one, two, three)ˇ b
 7967                a.f(one, two, three)ˇ b
 7968            "},
 7969        );
 7970    });
 7971}
 7972
 7973#[gpui::test]
 7974async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7975    init_test(cx, |_| {});
 7976
 7977    let fs = FakeFs::new(cx.executor());
 7978    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7979
 7980    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 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 buffer = project
 7996        .update(cx, |project, cx| {
 7997            project.open_local_buffer(path!("/file.rs"), cx)
 7998        })
 7999        .await
 8000        .unwrap();
 8001
 8002    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8003    let (editor, cx) = cx.add_window_view(|window, cx| {
 8004        build_editor_with_project(project.clone(), buffer, window, cx)
 8005    });
 8006    editor.update_in(cx, |editor, window, cx| {
 8007        editor.set_text("one\ntwo\nthree\n", window, cx)
 8008    });
 8009    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8010
 8011    cx.executor().start_waiting();
 8012    let fake_server = fake_servers.next().await.unwrap();
 8013
 8014    {
 8015        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8016            move |params, _| async move {
 8017                assert_eq!(
 8018                    params.text_document.uri,
 8019                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8020                );
 8021                assert_eq!(params.options.tab_size, 4);
 8022                Ok(Some(vec![lsp::TextEdit::new(
 8023                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8024                    ", ".to_string(),
 8025                )]))
 8026            },
 8027        );
 8028        let save = editor
 8029            .update_in(cx, |editor, window, cx| {
 8030                editor.save(true, project.clone(), window, cx)
 8031            })
 8032            .unwrap();
 8033        cx.executor().start_waiting();
 8034        save.await;
 8035
 8036        assert_eq!(
 8037            editor.update(cx, |editor, cx| editor.text(cx)),
 8038            "one, two\nthree\n"
 8039        );
 8040        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8041    }
 8042
 8043    {
 8044        editor.update_in(cx, |editor, window, cx| {
 8045            editor.set_text("one\ntwo\nthree\n", window, cx)
 8046        });
 8047        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8048
 8049        // Ensure we can still save even if formatting hangs.
 8050        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8051            move |params, _| async move {
 8052                assert_eq!(
 8053                    params.text_document.uri,
 8054                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8055                );
 8056                futures::future::pending::<()>().await;
 8057                unreachable!()
 8058            },
 8059        );
 8060        let save = editor
 8061            .update_in(cx, |editor, window, cx| {
 8062                editor.save(true, project.clone(), window, cx)
 8063            })
 8064            .unwrap();
 8065        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8066        cx.executor().start_waiting();
 8067        save.await;
 8068        assert_eq!(
 8069            editor.update(cx, |editor, cx| editor.text(cx)),
 8070            "one\ntwo\nthree\n"
 8071        );
 8072    }
 8073
 8074    // For non-dirty buffer, no formatting request should be sent
 8075    {
 8076        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8077
 8078        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8079            panic!("Should not be invoked on non-dirty buffer");
 8080        });
 8081        let save = editor
 8082            .update_in(cx, |editor, window, cx| {
 8083                editor.save(true, project.clone(), window, cx)
 8084            })
 8085            .unwrap();
 8086        cx.executor().start_waiting();
 8087        save.await;
 8088    }
 8089
 8090    // Set rust language override and assert overridden tabsize is sent to language server
 8091    update_test_language_settings(cx, |settings| {
 8092        settings.languages.insert(
 8093            "Rust".into(),
 8094            LanguageSettingsContent {
 8095                tab_size: NonZeroU32::new(8),
 8096                ..Default::default()
 8097            },
 8098        );
 8099    });
 8100
 8101    {
 8102        editor.update_in(cx, |editor, window, cx| {
 8103            editor.set_text("somehting_new\n", window, cx)
 8104        });
 8105        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8106        let _formatting_request_signal = fake_server
 8107            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8108                assert_eq!(
 8109                    params.text_document.uri,
 8110                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8111                );
 8112                assert_eq!(params.options.tab_size, 8);
 8113                Ok(Some(vec![]))
 8114            });
 8115        let save = editor
 8116            .update_in(cx, |editor, window, cx| {
 8117                editor.save(true, project.clone(), window, cx)
 8118            })
 8119            .unwrap();
 8120        cx.executor().start_waiting();
 8121        save.await;
 8122    }
 8123}
 8124
 8125#[gpui::test]
 8126async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8127    init_test(cx, |_| {});
 8128
 8129    let cols = 4;
 8130    let rows = 10;
 8131    let sample_text_1 = sample_text(rows, cols, 'a');
 8132    assert_eq!(
 8133        sample_text_1,
 8134        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8135    );
 8136    let sample_text_2 = sample_text(rows, cols, 'l');
 8137    assert_eq!(
 8138        sample_text_2,
 8139        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8140    );
 8141    let sample_text_3 = sample_text(rows, cols, 'v');
 8142    assert_eq!(
 8143        sample_text_3,
 8144        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8145    );
 8146
 8147    let fs = FakeFs::new(cx.executor());
 8148    fs.insert_tree(
 8149        path!("/a"),
 8150        json!({
 8151            "main.rs": sample_text_1,
 8152            "other.rs": sample_text_2,
 8153            "lib.rs": sample_text_3,
 8154        }),
 8155    )
 8156    .await;
 8157
 8158    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8159    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8160    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8161
 8162    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8163    language_registry.add(rust_lang());
 8164    let mut fake_servers = language_registry.register_fake_lsp(
 8165        "Rust",
 8166        FakeLspAdapter {
 8167            capabilities: lsp::ServerCapabilities {
 8168                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8169                ..Default::default()
 8170            },
 8171            ..Default::default()
 8172        },
 8173    );
 8174
 8175    let worktree = project.update(cx, |project, cx| {
 8176        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8177        assert_eq!(worktrees.len(), 1);
 8178        worktrees.pop().unwrap()
 8179    });
 8180    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8181
 8182    let buffer_1 = project
 8183        .update(cx, |project, cx| {
 8184            project.open_buffer((worktree_id, "main.rs"), cx)
 8185        })
 8186        .await
 8187        .unwrap();
 8188    let buffer_2 = project
 8189        .update(cx, |project, cx| {
 8190            project.open_buffer((worktree_id, "other.rs"), cx)
 8191        })
 8192        .await
 8193        .unwrap();
 8194    let buffer_3 = project
 8195        .update(cx, |project, cx| {
 8196            project.open_buffer((worktree_id, "lib.rs"), cx)
 8197        })
 8198        .await
 8199        .unwrap();
 8200
 8201    let multi_buffer = cx.new(|cx| {
 8202        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8203        multi_buffer.push_excerpts(
 8204            buffer_1.clone(),
 8205            [
 8206                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8207                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8208                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8209            ],
 8210            cx,
 8211        );
 8212        multi_buffer.push_excerpts(
 8213            buffer_2.clone(),
 8214            [
 8215                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8216                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8217                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8218            ],
 8219            cx,
 8220        );
 8221        multi_buffer.push_excerpts(
 8222            buffer_3.clone(),
 8223            [
 8224                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8225                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8226                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8227            ],
 8228            cx,
 8229        );
 8230        multi_buffer
 8231    });
 8232    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8233        Editor::new(
 8234            EditorMode::full(),
 8235            multi_buffer,
 8236            Some(project.clone()),
 8237            window,
 8238            cx,
 8239        )
 8240    });
 8241
 8242    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8243        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8244            s.select_ranges(Some(1..2))
 8245        });
 8246        editor.insert("|one|two|three|", window, cx);
 8247    });
 8248    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8249    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8250        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8251            s.select_ranges(Some(60..70))
 8252        });
 8253        editor.insert("|four|five|six|", window, cx);
 8254    });
 8255    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8256
 8257    // First two buffers should be edited, but not the third one.
 8258    assert_eq!(
 8259        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8260        "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}",
 8261    );
 8262    buffer_1.update(cx, |buffer, _| {
 8263        assert!(buffer.is_dirty());
 8264        assert_eq!(
 8265            buffer.text(),
 8266            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8267        )
 8268    });
 8269    buffer_2.update(cx, |buffer, _| {
 8270        assert!(buffer.is_dirty());
 8271        assert_eq!(
 8272            buffer.text(),
 8273            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8274        )
 8275    });
 8276    buffer_3.update(cx, |buffer, _| {
 8277        assert!(!buffer.is_dirty());
 8278        assert_eq!(buffer.text(), sample_text_3,)
 8279    });
 8280    cx.executor().run_until_parked();
 8281
 8282    cx.executor().start_waiting();
 8283    let save = multi_buffer_editor
 8284        .update_in(cx, |editor, window, cx| {
 8285            editor.save(true, project.clone(), window, cx)
 8286        })
 8287        .unwrap();
 8288
 8289    let fake_server = fake_servers.next().await.unwrap();
 8290    fake_server
 8291        .server
 8292        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8293            Ok(Some(vec![lsp::TextEdit::new(
 8294                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8295                format!("[{} formatted]", params.text_document.uri),
 8296            )]))
 8297        })
 8298        .detach();
 8299    save.await;
 8300
 8301    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8302    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8303    assert_eq!(
 8304        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8305        uri!(
 8306            "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}"
 8307        ),
 8308    );
 8309    buffer_1.update(cx, |buffer, _| {
 8310        assert!(!buffer.is_dirty());
 8311        assert_eq!(
 8312            buffer.text(),
 8313            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8314        )
 8315    });
 8316    buffer_2.update(cx, |buffer, _| {
 8317        assert!(!buffer.is_dirty());
 8318        assert_eq!(
 8319            buffer.text(),
 8320            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8321        )
 8322    });
 8323    buffer_3.update(cx, |buffer, _| {
 8324        assert!(!buffer.is_dirty());
 8325        assert_eq!(buffer.text(), sample_text_3,)
 8326    });
 8327}
 8328
 8329#[gpui::test]
 8330async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8331    init_test(cx, |_| {});
 8332
 8333    let fs = FakeFs::new(cx.executor());
 8334    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8335
 8336    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8337
 8338    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8339    language_registry.add(rust_lang());
 8340    let mut fake_servers = language_registry.register_fake_lsp(
 8341        "Rust",
 8342        FakeLspAdapter {
 8343            capabilities: lsp::ServerCapabilities {
 8344                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8345                ..Default::default()
 8346            },
 8347            ..Default::default()
 8348        },
 8349    );
 8350
 8351    let buffer = project
 8352        .update(cx, |project, cx| {
 8353            project.open_local_buffer(path!("/file.rs"), cx)
 8354        })
 8355        .await
 8356        .unwrap();
 8357
 8358    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8359    let (editor, cx) = cx.add_window_view(|window, cx| {
 8360        build_editor_with_project(project.clone(), buffer, window, cx)
 8361    });
 8362    editor.update_in(cx, |editor, window, cx| {
 8363        editor.set_text("one\ntwo\nthree\n", window, cx)
 8364    });
 8365    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8366
 8367    cx.executor().start_waiting();
 8368    let fake_server = fake_servers.next().await.unwrap();
 8369
 8370    let save = editor
 8371        .update_in(cx, |editor, window, cx| {
 8372            editor.save(true, project.clone(), window, cx)
 8373        })
 8374        .unwrap();
 8375    fake_server
 8376        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8377            assert_eq!(
 8378                params.text_document.uri,
 8379                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8380            );
 8381            assert_eq!(params.options.tab_size, 4);
 8382            Ok(Some(vec![lsp::TextEdit::new(
 8383                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8384                ", ".to_string(),
 8385            )]))
 8386        })
 8387        .next()
 8388        .await;
 8389    cx.executor().start_waiting();
 8390    save.await;
 8391    assert_eq!(
 8392        editor.update(cx, |editor, cx| editor.text(cx)),
 8393        "one, two\nthree\n"
 8394    );
 8395    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8396
 8397    editor.update_in(cx, |editor, window, cx| {
 8398        editor.set_text("one\ntwo\nthree\n", window, cx)
 8399    });
 8400    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8401
 8402    // Ensure we can still save even if formatting hangs.
 8403    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8404        move |params, _| async move {
 8405            assert_eq!(
 8406                params.text_document.uri,
 8407                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8408            );
 8409            futures::future::pending::<()>().await;
 8410            unreachable!()
 8411        },
 8412    );
 8413    let save = editor
 8414        .update_in(cx, |editor, window, cx| {
 8415            editor.save(true, project.clone(), window, cx)
 8416        })
 8417        .unwrap();
 8418    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8419    cx.executor().start_waiting();
 8420    save.await;
 8421    assert_eq!(
 8422        editor.update(cx, |editor, cx| editor.text(cx)),
 8423        "one\ntwo\nthree\n"
 8424    );
 8425    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8426
 8427    // For non-dirty buffer, no formatting request should be sent
 8428    let save = editor
 8429        .update_in(cx, |editor, window, cx| {
 8430            editor.save(true, project.clone(), window, cx)
 8431        })
 8432        .unwrap();
 8433    let _pending_format_request = fake_server
 8434        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8435            panic!("Should not be invoked on non-dirty buffer");
 8436        })
 8437        .next();
 8438    cx.executor().start_waiting();
 8439    save.await;
 8440
 8441    // Set Rust language override and assert overridden tabsize is sent to language server
 8442    update_test_language_settings(cx, |settings| {
 8443        settings.languages.insert(
 8444            "Rust".into(),
 8445            LanguageSettingsContent {
 8446                tab_size: NonZeroU32::new(8),
 8447                ..Default::default()
 8448            },
 8449        );
 8450    });
 8451
 8452    editor.update_in(cx, |editor, window, cx| {
 8453        editor.set_text("somehting_new\n", window, cx)
 8454    });
 8455    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8456    let save = editor
 8457        .update_in(cx, |editor, window, cx| {
 8458            editor.save(true, project.clone(), window, cx)
 8459        })
 8460        .unwrap();
 8461    fake_server
 8462        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8463            assert_eq!(
 8464                params.text_document.uri,
 8465                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8466            );
 8467            assert_eq!(params.options.tab_size, 8);
 8468            Ok(Some(vec![]))
 8469        })
 8470        .next()
 8471        .await;
 8472    cx.executor().start_waiting();
 8473    save.await;
 8474}
 8475
 8476#[gpui::test]
 8477async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8478    init_test(cx, |settings| {
 8479        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8480            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8481        ))
 8482    });
 8483
 8484    let fs = FakeFs::new(cx.executor());
 8485    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8486
 8487    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8488
 8489    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8490    language_registry.add(Arc::new(Language::new(
 8491        LanguageConfig {
 8492            name: "Rust".into(),
 8493            matcher: LanguageMatcher {
 8494                path_suffixes: vec!["rs".to_string()],
 8495                ..Default::default()
 8496            },
 8497            ..LanguageConfig::default()
 8498        },
 8499        Some(tree_sitter_rust::LANGUAGE.into()),
 8500    )));
 8501    update_test_language_settings(cx, |settings| {
 8502        // Enable Prettier formatting for the same buffer, and ensure
 8503        // LSP is called instead of Prettier.
 8504        settings.defaults.prettier = Some(PrettierSettings {
 8505            allowed: true,
 8506            ..PrettierSettings::default()
 8507        });
 8508    });
 8509    let mut fake_servers = language_registry.register_fake_lsp(
 8510        "Rust",
 8511        FakeLspAdapter {
 8512            capabilities: lsp::ServerCapabilities {
 8513                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8514                ..Default::default()
 8515            },
 8516            ..Default::default()
 8517        },
 8518    );
 8519
 8520    let buffer = project
 8521        .update(cx, |project, cx| {
 8522            project.open_local_buffer(path!("/file.rs"), cx)
 8523        })
 8524        .await
 8525        .unwrap();
 8526
 8527    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8528    let (editor, cx) = cx.add_window_view(|window, cx| {
 8529        build_editor_with_project(project.clone(), buffer, window, cx)
 8530    });
 8531    editor.update_in(cx, |editor, window, cx| {
 8532        editor.set_text("one\ntwo\nthree\n", window, cx)
 8533    });
 8534
 8535    cx.executor().start_waiting();
 8536    let fake_server = fake_servers.next().await.unwrap();
 8537
 8538    let format = editor
 8539        .update_in(cx, |editor, window, cx| {
 8540            editor.perform_format(
 8541                project.clone(),
 8542                FormatTrigger::Manual,
 8543                FormatTarget::Buffers,
 8544                window,
 8545                cx,
 8546            )
 8547        })
 8548        .unwrap();
 8549    fake_server
 8550        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8551            assert_eq!(
 8552                params.text_document.uri,
 8553                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8554            );
 8555            assert_eq!(params.options.tab_size, 4);
 8556            Ok(Some(vec![lsp::TextEdit::new(
 8557                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8558                ", ".to_string(),
 8559            )]))
 8560        })
 8561        .next()
 8562        .await;
 8563    cx.executor().start_waiting();
 8564    format.await;
 8565    assert_eq!(
 8566        editor.update(cx, |editor, cx| editor.text(cx)),
 8567        "one, two\nthree\n"
 8568    );
 8569
 8570    editor.update_in(cx, |editor, window, cx| {
 8571        editor.set_text("one\ntwo\nthree\n", window, cx)
 8572    });
 8573    // Ensure we don't lock if formatting hangs.
 8574    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8575        move |params, _| async move {
 8576            assert_eq!(
 8577                params.text_document.uri,
 8578                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8579            );
 8580            futures::future::pending::<()>().await;
 8581            unreachable!()
 8582        },
 8583    );
 8584    let format = editor
 8585        .update_in(cx, |editor, window, cx| {
 8586            editor.perform_format(
 8587                project,
 8588                FormatTrigger::Manual,
 8589                FormatTarget::Buffers,
 8590                window,
 8591                cx,
 8592            )
 8593        })
 8594        .unwrap();
 8595    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8596    cx.executor().start_waiting();
 8597    format.await;
 8598    assert_eq!(
 8599        editor.update(cx, |editor, cx| editor.text(cx)),
 8600        "one\ntwo\nthree\n"
 8601    );
 8602}
 8603
 8604#[gpui::test]
 8605async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8606    init_test(cx, |settings| {
 8607        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8608        settings.defaults.formatter =
 8609            Some(language_settings::SelectedFormatter::List(FormatterList(
 8610                vec![
 8611                    Formatter::LanguageServer { name: None },
 8612                    Formatter::CodeActions(
 8613                        [
 8614                            ("code-action-1".into(), true),
 8615                            ("code-action-2".into(), true),
 8616                        ]
 8617                        .into_iter()
 8618                        .collect(),
 8619                    ),
 8620                ]
 8621                .into(),
 8622            )))
 8623    });
 8624
 8625    let fs = FakeFs::new(cx.executor());
 8626    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8627        .await;
 8628
 8629    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8630    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8631    language_registry.add(rust_lang());
 8632
 8633    let mut fake_servers = language_registry.register_fake_lsp(
 8634        "Rust",
 8635        FakeLspAdapter {
 8636            capabilities: lsp::ServerCapabilities {
 8637                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8638                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8639                    commands: vec!["the-command-for-code-action-1".into()],
 8640                    ..Default::default()
 8641                }),
 8642                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8643                ..Default::default()
 8644            },
 8645            ..Default::default()
 8646        },
 8647    );
 8648
 8649    let buffer = project
 8650        .update(cx, |project, cx| {
 8651            project.open_local_buffer(path!("/file.rs"), cx)
 8652        })
 8653        .await
 8654        .unwrap();
 8655
 8656    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8657    let (editor, cx) = cx.add_window_view(|window, cx| {
 8658        build_editor_with_project(project.clone(), buffer, window, cx)
 8659    });
 8660
 8661    cx.executor().start_waiting();
 8662
 8663    let fake_server = fake_servers.next().await.unwrap();
 8664    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8665        move |_params, _| async move {
 8666            Ok(Some(vec![lsp::TextEdit::new(
 8667                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8668                "applied-formatting\n".to_string(),
 8669            )]))
 8670        },
 8671    );
 8672    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8673        move |params, _| async move {
 8674            assert_eq!(
 8675                params.context.only,
 8676                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8677            );
 8678            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8679            Ok(Some(vec![
 8680                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8681                    kind: Some("code-action-1".into()),
 8682                    edit: Some(lsp::WorkspaceEdit::new(
 8683                        [(
 8684                            uri.clone(),
 8685                            vec![lsp::TextEdit::new(
 8686                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8687                                "applied-code-action-1-edit\n".to_string(),
 8688                            )],
 8689                        )]
 8690                        .into_iter()
 8691                        .collect(),
 8692                    )),
 8693                    command: Some(lsp::Command {
 8694                        command: "the-command-for-code-action-1".into(),
 8695                        ..Default::default()
 8696                    }),
 8697                    ..Default::default()
 8698                }),
 8699                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8700                    kind: Some("code-action-2".into()),
 8701                    edit: Some(lsp::WorkspaceEdit::new(
 8702                        [(
 8703                            uri.clone(),
 8704                            vec![lsp::TextEdit::new(
 8705                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8706                                "applied-code-action-2-edit\n".to_string(),
 8707                            )],
 8708                        )]
 8709                        .into_iter()
 8710                        .collect(),
 8711                    )),
 8712                    ..Default::default()
 8713                }),
 8714            ]))
 8715        },
 8716    );
 8717
 8718    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8719        move |params, _| async move { Ok(params) }
 8720    });
 8721
 8722    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8723    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8724        let fake = fake_server.clone();
 8725        let lock = command_lock.clone();
 8726        move |params, _| {
 8727            assert_eq!(params.command, "the-command-for-code-action-1");
 8728            let fake = fake.clone();
 8729            let lock = lock.clone();
 8730            async move {
 8731                lock.lock().await;
 8732                fake.server
 8733                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8734                        label: None,
 8735                        edit: lsp::WorkspaceEdit {
 8736                            changes: Some(
 8737                                [(
 8738                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8739                                    vec![lsp::TextEdit {
 8740                                        range: lsp::Range::new(
 8741                                            lsp::Position::new(0, 0),
 8742                                            lsp::Position::new(0, 0),
 8743                                        ),
 8744                                        new_text: "applied-code-action-1-command\n".into(),
 8745                                    }],
 8746                                )]
 8747                                .into_iter()
 8748                                .collect(),
 8749                            ),
 8750                            ..Default::default()
 8751                        },
 8752                    })
 8753                    .await
 8754                    .unwrap();
 8755                Ok(Some(json!(null)))
 8756            }
 8757        }
 8758    });
 8759
 8760    cx.executor().start_waiting();
 8761    editor
 8762        .update_in(cx, |editor, window, cx| {
 8763            editor.perform_format(
 8764                project.clone(),
 8765                FormatTrigger::Manual,
 8766                FormatTarget::Buffers,
 8767                window,
 8768                cx,
 8769            )
 8770        })
 8771        .unwrap()
 8772        .await;
 8773    editor.update(cx, |editor, cx| {
 8774        assert_eq!(
 8775            editor.text(cx),
 8776            r#"
 8777                applied-code-action-2-edit
 8778                applied-code-action-1-command
 8779                applied-code-action-1-edit
 8780                applied-formatting
 8781                one
 8782                two
 8783                three
 8784            "#
 8785            .unindent()
 8786        );
 8787    });
 8788
 8789    editor.update_in(cx, |editor, window, cx| {
 8790        editor.undo(&Default::default(), window, cx);
 8791        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8792    });
 8793
 8794    // Perform a manual edit while waiting for an LSP command
 8795    // that's being run as part of a formatting code action.
 8796    let lock_guard = command_lock.lock().await;
 8797    let format = editor
 8798        .update_in(cx, |editor, window, cx| {
 8799            editor.perform_format(
 8800                project.clone(),
 8801                FormatTrigger::Manual,
 8802                FormatTarget::Buffers,
 8803                window,
 8804                cx,
 8805            )
 8806        })
 8807        .unwrap();
 8808    cx.run_until_parked();
 8809    editor.update(cx, |editor, cx| {
 8810        assert_eq!(
 8811            editor.text(cx),
 8812            r#"
 8813                applied-code-action-1-edit
 8814                applied-formatting
 8815                one
 8816                two
 8817                three
 8818            "#
 8819            .unindent()
 8820        );
 8821
 8822        editor.buffer.update(cx, |buffer, cx| {
 8823            let ix = buffer.len(cx);
 8824            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8825        });
 8826    });
 8827
 8828    // Allow the LSP command to proceed. Because the buffer was edited,
 8829    // the second code action will not be run.
 8830    drop(lock_guard);
 8831    format.await;
 8832    editor.update_in(cx, |editor, window, cx| {
 8833        assert_eq!(
 8834            editor.text(cx),
 8835            r#"
 8836                applied-code-action-1-command
 8837                applied-code-action-1-edit
 8838                applied-formatting
 8839                one
 8840                two
 8841                three
 8842                edited
 8843            "#
 8844            .unindent()
 8845        );
 8846
 8847        // The manual edit is undone first, because it is the last thing the user did
 8848        // (even though the command completed afterwards).
 8849        editor.undo(&Default::default(), window, cx);
 8850        assert_eq!(
 8851            editor.text(cx),
 8852            r#"
 8853                applied-code-action-1-command
 8854                applied-code-action-1-edit
 8855                applied-formatting
 8856                one
 8857                two
 8858                three
 8859            "#
 8860            .unindent()
 8861        );
 8862
 8863        // All the formatting (including the command, which completed after the manual edit)
 8864        // is undone together.
 8865        editor.undo(&Default::default(), window, cx);
 8866        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8867    });
 8868}
 8869
 8870#[gpui::test]
 8871async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8872    init_test(cx, |settings| {
 8873        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8874            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8875        ))
 8876    });
 8877
 8878    let fs = FakeFs::new(cx.executor());
 8879    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8880
 8881    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8882
 8883    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8884    language_registry.add(Arc::new(Language::new(
 8885        LanguageConfig {
 8886            name: "TypeScript".into(),
 8887            matcher: LanguageMatcher {
 8888                path_suffixes: vec!["ts".to_string()],
 8889                ..Default::default()
 8890            },
 8891            ..LanguageConfig::default()
 8892        },
 8893        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8894    )));
 8895    update_test_language_settings(cx, |settings| {
 8896        settings.defaults.prettier = Some(PrettierSettings {
 8897            allowed: true,
 8898            ..PrettierSettings::default()
 8899        });
 8900    });
 8901    let mut fake_servers = language_registry.register_fake_lsp(
 8902        "TypeScript",
 8903        FakeLspAdapter {
 8904            capabilities: lsp::ServerCapabilities {
 8905                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8906                ..Default::default()
 8907            },
 8908            ..Default::default()
 8909        },
 8910    );
 8911
 8912    let buffer = project
 8913        .update(cx, |project, cx| {
 8914            project.open_local_buffer(path!("/file.ts"), cx)
 8915        })
 8916        .await
 8917        .unwrap();
 8918
 8919    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8920    let (editor, cx) = cx.add_window_view(|window, cx| {
 8921        build_editor_with_project(project.clone(), buffer, window, cx)
 8922    });
 8923    editor.update_in(cx, |editor, window, cx| {
 8924        editor.set_text(
 8925            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8926            window,
 8927            cx,
 8928        )
 8929    });
 8930
 8931    cx.executor().start_waiting();
 8932    let fake_server = fake_servers.next().await.unwrap();
 8933
 8934    let format = editor
 8935        .update_in(cx, |editor, window, cx| {
 8936            editor.perform_code_action_kind(
 8937                project.clone(),
 8938                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8939                window,
 8940                cx,
 8941            )
 8942        })
 8943        .unwrap();
 8944    fake_server
 8945        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8946            assert_eq!(
 8947                params.text_document.uri,
 8948                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8949            );
 8950            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8951                lsp::CodeAction {
 8952                    title: "Organize Imports".to_string(),
 8953                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8954                    edit: Some(lsp::WorkspaceEdit {
 8955                        changes: Some(
 8956                            [(
 8957                                params.text_document.uri.clone(),
 8958                                vec![lsp::TextEdit::new(
 8959                                    lsp::Range::new(
 8960                                        lsp::Position::new(1, 0),
 8961                                        lsp::Position::new(2, 0),
 8962                                    ),
 8963                                    "".to_string(),
 8964                                )],
 8965                            )]
 8966                            .into_iter()
 8967                            .collect(),
 8968                        ),
 8969                        ..Default::default()
 8970                    }),
 8971                    ..Default::default()
 8972                },
 8973            )]))
 8974        })
 8975        .next()
 8976        .await;
 8977    cx.executor().start_waiting();
 8978    format.await;
 8979    assert_eq!(
 8980        editor.update(cx, |editor, cx| editor.text(cx)),
 8981        "import { a } from 'module';\n\nconst x = a;\n"
 8982    );
 8983
 8984    editor.update_in(cx, |editor, window, cx| {
 8985        editor.set_text(
 8986            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8987            window,
 8988            cx,
 8989        )
 8990    });
 8991    // Ensure we don't lock if code action hangs.
 8992    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8993        move |params, _| async move {
 8994            assert_eq!(
 8995                params.text_document.uri,
 8996                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8997            );
 8998            futures::future::pending::<()>().await;
 8999            unreachable!()
 9000        },
 9001    );
 9002    let format = editor
 9003        .update_in(cx, |editor, window, cx| {
 9004            editor.perform_code_action_kind(
 9005                project,
 9006                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9007                window,
 9008                cx,
 9009            )
 9010        })
 9011        .unwrap();
 9012    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9013    cx.executor().start_waiting();
 9014    format.await;
 9015    assert_eq!(
 9016        editor.update(cx, |editor, cx| editor.text(cx)),
 9017        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9018    );
 9019}
 9020
 9021#[gpui::test]
 9022async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9023    init_test(cx, |_| {});
 9024
 9025    let mut cx = EditorLspTestContext::new_rust(
 9026        lsp::ServerCapabilities {
 9027            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9028            ..Default::default()
 9029        },
 9030        cx,
 9031    )
 9032    .await;
 9033
 9034    cx.set_state(indoc! {"
 9035        one.twoˇ
 9036    "});
 9037
 9038    // The format request takes a long time. When it completes, it inserts
 9039    // a newline and an indent before the `.`
 9040    cx.lsp
 9041        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9042            let executor = cx.background_executor().clone();
 9043            async move {
 9044                executor.timer(Duration::from_millis(100)).await;
 9045                Ok(Some(vec![lsp::TextEdit {
 9046                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9047                    new_text: "\n    ".into(),
 9048                }]))
 9049            }
 9050        });
 9051
 9052    // Submit a format request.
 9053    let format_1 = cx
 9054        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9055        .unwrap();
 9056    cx.executor().run_until_parked();
 9057
 9058    // Submit a second format request.
 9059    let format_2 = cx
 9060        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9061        .unwrap();
 9062    cx.executor().run_until_parked();
 9063
 9064    // Wait for both format requests to complete
 9065    cx.executor().advance_clock(Duration::from_millis(200));
 9066    cx.executor().start_waiting();
 9067    format_1.await.unwrap();
 9068    cx.executor().start_waiting();
 9069    format_2.await.unwrap();
 9070
 9071    // The formatting edits only happens once.
 9072    cx.assert_editor_state(indoc! {"
 9073        one
 9074            .twoˇ
 9075    "});
 9076}
 9077
 9078#[gpui::test]
 9079async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9080    init_test(cx, |settings| {
 9081        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9082    });
 9083
 9084    let mut cx = EditorLspTestContext::new_rust(
 9085        lsp::ServerCapabilities {
 9086            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9087            ..Default::default()
 9088        },
 9089        cx,
 9090    )
 9091    .await;
 9092
 9093    // Set up a buffer white some trailing whitespace and no trailing newline.
 9094    cx.set_state(
 9095        &[
 9096            "one ",   //
 9097            "twoˇ",   //
 9098            "three ", //
 9099            "four",   //
 9100        ]
 9101        .join("\n"),
 9102    );
 9103
 9104    // Submit a format request.
 9105    let format = cx
 9106        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9107        .unwrap();
 9108
 9109    // Record which buffer changes have been sent to the language server
 9110    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9111    cx.lsp
 9112        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9113            let buffer_changes = buffer_changes.clone();
 9114            move |params, _| {
 9115                buffer_changes.lock().extend(
 9116                    params
 9117                        .content_changes
 9118                        .into_iter()
 9119                        .map(|e| (e.range.unwrap(), e.text)),
 9120                );
 9121            }
 9122        });
 9123
 9124    // Handle formatting requests to the language server.
 9125    cx.lsp
 9126        .set_request_handler::<lsp::request::Formatting, _, _>({
 9127            let buffer_changes = buffer_changes.clone();
 9128            move |_, _| {
 9129                // When formatting is requested, trailing whitespace has already been stripped,
 9130                // and the trailing newline has already been added.
 9131                assert_eq!(
 9132                    &buffer_changes.lock()[1..],
 9133                    &[
 9134                        (
 9135                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9136                            "".into()
 9137                        ),
 9138                        (
 9139                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9140                            "".into()
 9141                        ),
 9142                        (
 9143                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9144                            "\n".into()
 9145                        ),
 9146                    ]
 9147                );
 9148
 9149                // Insert blank lines between each line of the buffer.
 9150                async move {
 9151                    Ok(Some(vec![
 9152                        lsp::TextEdit {
 9153                            range: lsp::Range::new(
 9154                                lsp::Position::new(1, 0),
 9155                                lsp::Position::new(1, 0),
 9156                            ),
 9157                            new_text: "\n".into(),
 9158                        },
 9159                        lsp::TextEdit {
 9160                            range: lsp::Range::new(
 9161                                lsp::Position::new(2, 0),
 9162                                lsp::Position::new(2, 0),
 9163                            ),
 9164                            new_text: "\n".into(),
 9165                        },
 9166                    ]))
 9167                }
 9168            }
 9169        });
 9170
 9171    // After formatting the buffer, the trailing whitespace is stripped,
 9172    // a newline is appended, and the edits provided by the language server
 9173    // have been applied.
 9174    format.await.unwrap();
 9175    cx.assert_editor_state(
 9176        &[
 9177            "one",   //
 9178            "",      //
 9179            "twoˇ",  //
 9180            "",      //
 9181            "three", //
 9182            "four",  //
 9183            "",      //
 9184        ]
 9185        .join("\n"),
 9186    );
 9187
 9188    // Undoing the formatting undoes the trailing whitespace removal, the
 9189    // trailing newline, and the LSP edits.
 9190    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9191    cx.assert_editor_state(
 9192        &[
 9193            "one ",   //
 9194            "twoˇ",   //
 9195            "three ", //
 9196            "four",   //
 9197        ]
 9198        .join("\n"),
 9199    );
 9200}
 9201
 9202#[gpui::test]
 9203async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9204    cx: &mut TestAppContext,
 9205) {
 9206    init_test(cx, |_| {});
 9207
 9208    cx.update(|cx| {
 9209        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9210            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9211                settings.auto_signature_help = Some(true);
 9212            });
 9213        });
 9214    });
 9215
 9216    let mut cx = EditorLspTestContext::new_rust(
 9217        lsp::ServerCapabilities {
 9218            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9219                ..Default::default()
 9220            }),
 9221            ..Default::default()
 9222        },
 9223        cx,
 9224    )
 9225    .await;
 9226
 9227    let language = Language::new(
 9228        LanguageConfig {
 9229            name: "Rust".into(),
 9230            brackets: BracketPairConfig {
 9231                pairs: vec![
 9232                    BracketPair {
 9233                        start: "{".to_string(),
 9234                        end: "}".to_string(),
 9235                        close: true,
 9236                        surround: true,
 9237                        newline: true,
 9238                    },
 9239                    BracketPair {
 9240                        start: "(".to_string(),
 9241                        end: ")".to_string(),
 9242                        close: true,
 9243                        surround: true,
 9244                        newline: true,
 9245                    },
 9246                    BracketPair {
 9247                        start: "/*".to_string(),
 9248                        end: " */".to_string(),
 9249                        close: true,
 9250                        surround: true,
 9251                        newline: true,
 9252                    },
 9253                    BracketPair {
 9254                        start: "[".to_string(),
 9255                        end: "]".to_string(),
 9256                        close: false,
 9257                        surround: false,
 9258                        newline: true,
 9259                    },
 9260                    BracketPair {
 9261                        start: "\"".to_string(),
 9262                        end: "\"".to_string(),
 9263                        close: true,
 9264                        surround: true,
 9265                        newline: false,
 9266                    },
 9267                    BracketPair {
 9268                        start: "<".to_string(),
 9269                        end: ">".to_string(),
 9270                        close: false,
 9271                        surround: true,
 9272                        newline: true,
 9273                    },
 9274                ],
 9275                ..Default::default()
 9276            },
 9277            autoclose_before: "})]".to_string(),
 9278            ..Default::default()
 9279        },
 9280        Some(tree_sitter_rust::LANGUAGE.into()),
 9281    );
 9282    let language = Arc::new(language);
 9283
 9284    cx.language_registry().add(language.clone());
 9285    cx.update_buffer(|buffer, cx| {
 9286        buffer.set_language(Some(language), cx);
 9287    });
 9288
 9289    cx.set_state(
 9290        &r#"
 9291            fn main() {
 9292                sampleˇ
 9293            }
 9294        "#
 9295        .unindent(),
 9296    );
 9297
 9298    cx.update_editor(|editor, window, cx| {
 9299        editor.handle_input("(", window, cx);
 9300    });
 9301    cx.assert_editor_state(
 9302        &"
 9303            fn main() {
 9304                sample(ˇ)
 9305            }
 9306        "
 9307        .unindent(),
 9308    );
 9309
 9310    let mocked_response = lsp::SignatureHelp {
 9311        signatures: vec![lsp::SignatureInformation {
 9312            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9313            documentation: None,
 9314            parameters: Some(vec![
 9315                lsp::ParameterInformation {
 9316                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9317                    documentation: None,
 9318                },
 9319                lsp::ParameterInformation {
 9320                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9321                    documentation: None,
 9322                },
 9323            ]),
 9324            active_parameter: None,
 9325        }],
 9326        active_signature: Some(0),
 9327        active_parameter: Some(0),
 9328    };
 9329    handle_signature_help_request(&mut cx, mocked_response).await;
 9330
 9331    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9332        .await;
 9333
 9334    cx.editor(|editor, _, _| {
 9335        let signature_help_state = editor.signature_help_state.popover().cloned();
 9336        assert_eq!(
 9337            signature_help_state.unwrap().label,
 9338            "param1: u8, param2: u8"
 9339        );
 9340    });
 9341}
 9342
 9343#[gpui::test]
 9344async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9345    init_test(cx, |_| {});
 9346
 9347    cx.update(|cx| {
 9348        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9349            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9350                settings.auto_signature_help = Some(false);
 9351                settings.show_signature_help_after_edits = Some(false);
 9352            });
 9353        });
 9354    });
 9355
 9356    let mut cx = EditorLspTestContext::new_rust(
 9357        lsp::ServerCapabilities {
 9358            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9359                ..Default::default()
 9360            }),
 9361            ..Default::default()
 9362        },
 9363        cx,
 9364    )
 9365    .await;
 9366
 9367    let language = Language::new(
 9368        LanguageConfig {
 9369            name: "Rust".into(),
 9370            brackets: BracketPairConfig {
 9371                pairs: vec![
 9372                    BracketPair {
 9373                        start: "{".to_string(),
 9374                        end: "}".to_string(),
 9375                        close: true,
 9376                        surround: true,
 9377                        newline: true,
 9378                    },
 9379                    BracketPair {
 9380                        start: "(".to_string(),
 9381                        end: ")".to_string(),
 9382                        close: true,
 9383                        surround: true,
 9384                        newline: true,
 9385                    },
 9386                    BracketPair {
 9387                        start: "/*".to_string(),
 9388                        end: " */".to_string(),
 9389                        close: true,
 9390                        surround: true,
 9391                        newline: true,
 9392                    },
 9393                    BracketPair {
 9394                        start: "[".to_string(),
 9395                        end: "]".to_string(),
 9396                        close: false,
 9397                        surround: false,
 9398                        newline: true,
 9399                    },
 9400                    BracketPair {
 9401                        start: "\"".to_string(),
 9402                        end: "\"".to_string(),
 9403                        close: true,
 9404                        surround: true,
 9405                        newline: false,
 9406                    },
 9407                    BracketPair {
 9408                        start: "<".to_string(),
 9409                        end: ">".to_string(),
 9410                        close: false,
 9411                        surround: true,
 9412                        newline: true,
 9413                    },
 9414                ],
 9415                ..Default::default()
 9416            },
 9417            autoclose_before: "})]".to_string(),
 9418            ..Default::default()
 9419        },
 9420        Some(tree_sitter_rust::LANGUAGE.into()),
 9421    );
 9422    let language = Arc::new(language);
 9423
 9424    cx.language_registry().add(language.clone());
 9425    cx.update_buffer(|buffer, cx| {
 9426        buffer.set_language(Some(language), cx);
 9427    });
 9428
 9429    // Ensure that signature_help is not called when no signature help is enabled.
 9430    cx.set_state(
 9431        &r#"
 9432            fn main() {
 9433                sampleˇ
 9434            }
 9435        "#
 9436        .unindent(),
 9437    );
 9438    cx.update_editor(|editor, window, cx| {
 9439        editor.handle_input("(", window, cx);
 9440    });
 9441    cx.assert_editor_state(
 9442        &"
 9443            fn main() {
 9444                sample(ˇ)
 9445            }
 9446        "
 9447        .unindent(),
 9448    );
 9449    cx.editor(|editor, _, _| {
 9450        assert!(editor.signature_help_state.task().is_none());
 9451    });
 9452
 9453    let mocked_response = lsp::SignatureHelp {
 9454        signatures: vec![lsp::SignatureInformation {
 9455            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9456            documentation: None,
 9457            parameters: Some(vec![
 9458                lsp::ParameterInformation {
 9459                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9460                    documentation: None,
 9461                },
 9462                lsp::ParameterInformation {
 9463                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9464                    documentation: None,
 9465                },
 9466            ]),
 9467            active_parameter: None,
 9468        }],
 9469        active_signature: Some(0),
 9470        active_parameter: Some(0),
 9471    };
 9472
 9473    // Ensure that signature_help is called when enabled afte edits
 9474    cx.update(|_, cx| {
 9475        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9476            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9477                settings.auto_signature_help = Some(false);
 9478                settings.show_signature_help_after_edits = Some(true);
 9479            });
 9480        });
 9481    });
 9482    cx.set_state(
 9483        &r#"
 9484            fn main() {
 9485                sampleˇ
 9486            }
 9487        "#
 9488        .unindent(),
 9489    );
 9490    cx.update_editor(|editor, window, cx| {
 9491        editor.handle_input("(", window, cx);
 9492    });
 9493    cx.assert_editor_state(
 9494        &"
 9495            fn main() {
 9496                sample(ˇ)
 9497            }
 9498        "
 9499        .unindent(),
 9500    );
 9501    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9502    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9503        .await;
 9504    cx.update_editor(|editor, _, _| {
 9505        let signature_help_state = editor.signature_help_state.popover().cloned();
 9506        assert!(signature_help_state.is_some());
 9507        assert_eq!(
 9508            signature_help_state.unwrap().label,
 9509            "param1: u8, param2: u8"
 9510        );
 9511        editor.signature_help_state = SignatureHelpState::default();
 9512    });
 9513
 9514    // Ensure that signature_help is called when auto signature help override is enabled
 9515    cx.update(|_, cx| {
 9516        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9517            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9518                settings.auto_signature_help = Some(true);
 9519                settings.show_signature_help_after_edits = Some(false);
 9520            });
 9521        });
 9522    });
 9523    cx.set_state(
 9524        &r#"
 9525            fn main() {
 9526                sampleˇ
 9527            }
 9528        "#
 9529        .unindent(),
 9530    );
 9531    cx.update_editor(|editor, window, cx| {
 9532        editor.handle_input("(", window, cx);
 9533    });
 9534    cx.assert_editor_state(
 9535        &"
 9536            fn main() {
 9537                sample(ˇ)
 9538            }
 9539        "
 9540        .unindent(),
 9541    );
 9542    handle_signature_help_request(&mut cx, mocked_response).await;
 9543    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9544        .await;
 9545    cx.editor(|editor, _, _| {
 9546        let signature_help_state = editor.signature_help_state.popover().cloned();
 9547        assert!(signature_help_state.is_some());
 9548        assert_eq!(
 9549            signature_help_state.unwrap().label,
 9550            "param1: u8, param2: u8"
 9551        );
 9552    });
 9553}
 9554
 9555#[gpui::test]
 9556async fn test_signature_help(cx: &mut TestAppContext) {
 9557    init_test(cx, |_| {});
 9558    cx.update(|cx| {
 9559        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9560            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9561                settings.auto_signature_help = Some(true);
 9562            });
 9563        });
 9564    });
 9565
 9566    let mut cx = EditorLspTestContext::new_rust(
 9567        lsp::ServerCapabilities {
 9568            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9569                ..Default::default()
 9570            }),
 9571            ..Default::default()
 9572        },
 9573        cx,
 9574    )
 9575    .await;
 9576
 9577    // A test that directly calls `show_signature_help`
 9578    cx.update_editor(|editor, window, cx| {
 9579        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 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(0),
 9600    };
 9601    handle_signature_help_request(&mut cx, mocked_response).await;
 9602
 9603    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9604        .await;
 9605
 9606    cx.editor(|editor, _, _| {
 9607        let signature_help_state = editor.signature_help_state.popover().cloned();
 9608        assert!(signature_help_state.is_some());
 9609        assert_eq!(
 9610            signature_help_state.unwrap().label,
 9611            "param1: u8, param2: u8"
 9612        );
 9613    });
 9614
 9615    // When exiting outside from inside the brackets, `signature_help` is closed.
 9616    cx.set_state(indoc! {"
 9617        fn main() {
 9618            sample(ˇ);
 9619        }
 9620
 9621        fn sample(param1: u8, param2: u8) {}
 9622    "});
 9623
 9624    cx.update_editor(|editor, window, cx| {
 9625        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9626    });
 9627
 9628    let mocked_response = lsp::SignatureHelp {
 9629        signatures: Vec::new(),
 9630        active_signature: None,
 9631        active_parameter: None,
 9632    };
 9633    handle_signature_help_request(&mut cx, mocked_response).await;
 9634
 9635    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9636        .await;
 9637
 9638    cx.editor(|editor, _, _| {
 9639        assert!(!editor.signature_help_state.is_shown());
 9640    });
 9641
 9642    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9643    cx.set_state(indoc! {"
 9644        fn main() {
 9645            sample(ˇ);
 9646        }
 9647
 9648        fn sample(param1: u8, param2: u8) {}
 9649    "});
 9650
 9651    let mocked_response = lsp::SignatureHelp {
 9652        signatures: vec![lsp::SignatureInformation {
 9653            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9654            documentation: None,
 9655            parameters: Some(vec![
 9656                lsp::ParameterInformation {
 9657                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9658                    documentation: None,
 9659                },
 9660                lsp::ParameterInformation {
 9661                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9662                    documentation: None,
 9663                },
 9664            ]),
 9665            active_parameter: None,
 9666        }],
 9667        active_signature: Some(0),
 9668        active_parameter: Some(0),
 9669    };
 9670    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9671    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9672        .await;
 9673    cx.editor(|editor, _, _| {
 9674        assert!(editor.signature_help_state.is_shown());
 9675    });
 9676
 9677    // Restore the popover with more parameter input
 9678    cx.set_state(indoc! {"
 9679        fn main() {
 9680            sample(param1, param2ˇ);
 9681        }
 9682
 9683        fn sample(param1: u8, param2: u8) {}
 9684    "});
 9685
 9686    let mocked_response = lsp::SignatureHelp {
 9687        signatures: vec![lsp::SignatureInformation {
 9688            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9689            documentation: None,
 9690            parameters: Some(vec![
 9691                lsp::ParameterInformation {
 9692                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9693                    documentation: None,
 9694                },
 9695                lsp::ParameterInformation {
 9696                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9697                    documentation: None,
 9698                },
 9699            ]),
 9700            active_parameter: None,
 9701        }],
 9702        active_signature: Some(0),
 9703        active_parameter: Some(1),
 9704    };
 9705    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9706    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9707        .await;
 9708
 9709    // When selecting a range, the popover is gone.
 9710    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9711    cx.update_editor(|editor, window, cx| {
 9712        editor.change_selections(None, window, cx, |s| {
 9713            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9714        })
 9715    });
 9716    cx.assert_editor_state(indoc! {"
 9717        fn main() {
 9718            sample(param1, «ˇparam2»);
 9719        }
 9720
 9721        fn sample(param1: u8, param2: u8) {}
 9722    "});
 9723    cx.editor(|editor, _, _| {
 9724        assert!(!editor.signature_help_state.is_shown());
 9725    });
 9726
 9727    // When unselecting again, the popover is back if within the brackets.
 9728    cx.update_editor(|editor, window, cx| {
 9729        editor.change_selections(None, window, cx, |s| {
 9730            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9731        })
 9732    });
 9733    cx.assert_editor_state(indoc! {"
 9734        fn main() {
 9735            sample(param1, ˇparam2);
 9736        }
 9737
 9738        fn sample(param1: u8, param2: u8) {}
 9739    "});
 9740    handle_signature_help_request(&mut cx, mocked_response).await;
 9741    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9742        .await;
 9743    cx.editor(|editor, _, _| {
 9744        assert!(editor.signature_help_state.is_shown());
 9745    });
 9746
 9747    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9748    cx.update_editor(|editor, window, cx| {
 9749        editor.change_selections(None, window, cx, |s| {
 9750            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9751            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9752        })
 9753    });
 9754    cx.assert_editor_state(indoc! {"
 9755        fn main() {
 9756            sample(param1, ˇparam2);
 9757        }
 9758
 9759        fn sample(param1: u8, param2: u8) {}
 9760    "});
 9761
 9762    let mocked_response = lsp::SignatureHelp {
 9763        signatures: vec![lsp::SignatureInformation {
 9764            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9765            documentation: None,
 9766            parameters: Some(vec![
 9767                lsp::ParameterInformation {
 9768                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9769                    documentation: None,
 9770                },
 9771                lsp::ParameterInformation {
 9772                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9773                    documentation: None,
 9774                },
 9775            ]),
 9776            active_parameter: None,
 9777        }],
 9778        active_signature: Some(0),
 9779        active_parameter: Some(1),
 9780    };
 9781    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9782    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9783        .await;
 9784    cx.update_editor(|editor, _, cx| {
 9785        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9786    });
 9787    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9788        .await;
 9789    cx.update_editor(|editor, window, cx| {
 9790        editor.change_selections(None, window, cx, |s| {
 9791            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9792        })
 9793    });
 9794    cx.assert_editor_state(indoc! {"
 9795        fn main() {
 9796            sample(param1, «ˇparam2»);
 9797        }
 9798
 9799        fn sample(param1: u8, param2: u8) {}
 9800    "});
 9801    cx.update_editor(|editor, window, cx| {
 9802        editor.change_selections(None, window, cx, |s| {
 9803            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9804        })
 9805    });
 9806    cx.assert_editor_state(indoc! {"
 9807        fn main() {
 9808            sample(param1, ˇparam2);
 9809        }
 9810
 9811        fn sample(param1: u8, param2: u8) {}
 9812    "});
 9813    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9814        .await;
 9815}
 9816
 9817#[gpui::test]
 9818async fn test_completion_mode(cx: &mut TestAppContext) {
 9819    init_test(cx, |_| {});
 9820    let mut cx = EditorLspTestContext::new_rust(
 9821        lsp::ServerCapabilities {
 9822            completion_provider: Some(lsp::CompletionOptions {
 9823                resolve_provider: Some(true),
 9824                ..Default::default()
 9825            }),
 9826            ..Default::default()
 9827        },
 9828        cx,
 9829    )
 9830    .await;
 9831
 9832    struct Run {
 9833        run_description: &'static str,
 9834        initial_state: String,
 9835        buffer_marked_text: String,
 9836        completion_text: &'static str,
 9837        expected_with_insert_mode: String,
 9838        expected_with_replace_mode: String,
 9839        expected_with_replace_subsequence_mode: String,
 9840        expected_with_replace_suffix_mode: String,
 9841    }
 9842
 9843    let runs = [
 9844        Run {
 9845            run_description: "Start of word matches completion text",
 9846            initial_state: "before ediˇ after".into(),
 9847            buffer_marked_text: "before <edi|> after".into(),
 9848            completion_text: "editor",
 9849            expected_with_insert_mode: "before editorˇ after".into(),
 9850            expected_with_replace_mode: "before editorˇ after".into(),
 9851            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9852            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9853        },
 9854        Run {
 9855            run_description: "Accept same text at the middle of the word",
 9856            initial_state: "before ediˇtor after".into(),
 9857            buffer_marked_text: "before <edi|tor> after".into(),
 9858            completion_text: "editor",
 9859            expected_with_insert_mode: "before editorˇtor after".into(),
 9860            expected_with_replace_mode: "before editorˇ after".into(),
 9861            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9862            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9863        },
 9864        Run {
 9865            run_description: "End of word matches completion text -- cursor at end",
 9866            initial_state: "before torˇ after".into(),
 9867            buffer_marked_text: "before <tor|> after".into(),
 9868            completion_text: "editor",
 9869            expected_with_insert_mode: "before editorˇ after".into(),
 9870            expected_with_replace_mode: "before editorˇ after".into(),
 9871            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9872            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9873        },
 9874        Run {
 9875            run_description: "End of word matches completion text -- cursor at start",
 9876            initial_state: "before ˇtor after".into(),
 9877            buffer_marked_text: "before <|tor> after".into(),
 9878            completion_text: "editor",
 9879            expected_with_insert_mode: "before editorˇtor after".into(),
 9880            expected_with_replace_mode: "before editorˇ after".into(),
 9881            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9882            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9883        },
 9884        Run {
 9885            run_description: "Prepend text containing whitespace",
 9886            initial_state: "pˇfield: bool".into(),
 9887            buffer_marked_text: "<p|field>: bool".into(),
 9888            completion_text: "pub ",
 9889            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9890            expected_with_replace_mode: "pub ˇ: bool".into(),
 9891            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9892            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9893        },
 9894        Run {
 9895            run_description: "Add element to start of list",
 9896            initial_state: "[element_ˇelement_2]".into(),
 9897            buffer_marked_text: "[<element_|element_2>]".into(),
 9898            completion_text: "element_1",
 9899            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9900            expected_with_replace_mode: "[element_1ˇ]".into(),
 9901            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9902            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9903        },
 9904        Run {
 9905            run_description: "Add element to start of list -- first and second elements are equal",
 9906            initial_state: "[elˇelement]".into(),
 9907            buffer_marked_text: "[<el|element>]".into(),
 9908            completion_text: "element",
 9909            expected_with_insert_mode: "[elementˇelement]".into(),
 9910            expected_with_replace_mode: "[elementˇ]".into(),
 9911            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9912            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9913        },
 9914        Run {
 9915            run_description: "Ends with matching suffix",
 9916            initial_state: "SubˇError".into(),
 9917            buffer_marked_text: "<Sub|Error>".into(),
 9918            completion_text: "SubscriptionError",
 9919            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9920            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9921            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9922            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9923        },
 9924        Run {
 9925            run_description: "Suffix is a subsequence -- contiguous",
 9926            initial_state: "SubˇErr".into(),
 9927            buffer_marked_text: "<Sub|Err>".into(),
 9928            completion_text: "SubscriptionError",
 9929            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9930            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9931            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9932            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9933        },
 9934        Run {
 9935            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9936            initial_state: "Suˇscrirr".into(),
 9937            buffer_marked_text: "<Su|scrirr>".into(),
 9938            completion_text: "SubscriptionError",
 9939            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9940            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9941            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9942            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9943        },
 9944        Run {
 9945            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9946            initial_state: "foo(indˇix)".into(),
 9947            buffer_marked_text: "foo(<ind|ix>)".into(),
 9948            completion_text: "node_index",
 9949            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9950            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9951            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9952            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9953        },
 9954    ];
 9955
 9956    for run in runs {
 9957        let run_variations = [
 9958            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9959            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9960            (
 9961                LspInsertMode::ReplaceSubsequence,
 9962                run.expected_with_replace_subsequence_mode,
 9963            ),
 9964            (
 9965                LspInsertMode::ReplaceSuffix,
 9966                run.expected_with_replace_suffix_mode,
 9967            ),
 9968        ];
 9969
 9970        for (lsp_insert_mode, expected_text) in run_variations {
 9971            eprintln!(
 9972                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9973                run.run_description,
 9974            );
 9975
 9976            update_test_language_settings(&mut cx, |settings| {
 9977                settings.defaults.completions = Some(CompletionSettings {
 9978                    lsp_insert_mode,
 9979                    words: WordsCompletionMode::Disabled,
 9980                    lsp: true,
 9981                    lsp_fetch_timeout_ms: 0,
 9982                });
 9983            });
 9984
 9985            cx.set_state(&run.initial_state);
 9986            cx.update_editor(|editor, window, cx| {
 9987                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9988            });
 9989
 9990            let counter = Arc::new(AtomicUsize::new(0));
 9991            handle_completion_request_with_insert_and_replace(
 9992                &mut cx,
 9993                &run.buffer_marked_text,
 9994                vec![run.completion_text],
 9995                counter.clone(),
 9996            )
 9997            .await;
 9998            cx.condition(|editor, _| editor.context_menu_visible())
 9999                .await;
10000            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10001
10002            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10003                editor
10004                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10005                    .unwrap()
10006            });
10007            cx.assert_editor_state(&expected_text);
10008            handle_resolve_completion_request(&mut cx, None).await;
10009            apply_additional_edits.await.unwrap();
10010        }
10011    }
10012}
10013
10014#[gpui::test]
10015async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10016    init_test(cx, |_| {});
10017    let mut cx = EditorLspTestContext::new_rust(
10018        lsp::ServerCapabilities {
10019            completion_provider: Some(lsp::CompletionOptions {
10020                resolve_provider: Some(true),
10021                ..Default::default()
10022            }),
10023            ..Default::default()
10024        },
10025        cx,
10026    )
10027    .await;
10028
10029    let initial_state = "SubˇError";
10030    let buffer_marked_text = "<Sub|Error>";
10031    let completion_text = "SubscriptionError";
10032    let expected_with_insert_mode = "SubscriptionErrorˇError";
10033    let expected_with_replace_mode = "SubscriptionErrorˇ";
10034
10035    update_test_language_settings(&mut cx, |settings| {
10036        settings.defaults.completions = Some(CompletionSettings {
10037            words: WordsCompletionMode::Disabled,
10038            // set the opposite here to ensure that the action is overriding the default behavior
10039            lsp_insert_mode: LspInsertMode::Insert,
10040            lsp: true,
10041            lsp_fetch_timeout_ms: 0,
10042        });
10043    });
10044
10045    cx.set_state(initial_state);
10046    cx.update_editor(|editor, window, cx| {
10047        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10048    });
10049
10050    let counter = Arc::new(AtomicUsize::new(0));
10051    handle_completion_request_with_insert_and_replace(
10052        &mut cx,
10053        &buffer_marked_text,
10054        vec![completion_text],
10055        counter.clone(),
10056    )
10057    .await;
10058    cx.condition(|editor, _| editor.context_menu_visible())
10059        .await;
10060    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10061
10062    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10063        editor
10064            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10065            .unwrap()
10066    });
10067    cx.assert_editor_state(&expected_with_replace_mode);
10068    handle_resolve_completion_request(&mut cx, None).await;
10069    apply_additional_edits.await.unwrap();
10070
10071    update_test_language_settings(&mut cx, |settings| {
10072        settings.defaults.completions = Some(CompletionSettings {
10073            words: WordsCompletionMode::Disabled,
10074            // set the opposite here to ensure that the action is overriding the default behavior
10075            lsp_insert_mode: LspInsertMode::Replace,
10076            lsp: true,
10077            lsp_fetch_timeout_ms: 0,
10078        });
10079    });
10080
10081    cx.set_state(initial_state);
10082    cx.update_editor(|editor, window, cx| {
10083        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10084    });
10085    handle_completion_request_with_insert_and_replace(
10086        &mut cx,
10087        &buffer_marked_text,
10088        vec![completion_text],
10089        counter.clone(),
10090    )
10091    .await;
10092    cx.condition(|editor, _| editor.context_menu_visible())
10093        .await;
10094    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10095
10096    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10097        editor
10098            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10099            .unwrap()
10100    });
10101    cx.assert_editor_state(&expected_with_insert_mode);
10102    handle_resolve_completion_request(&mut cx, None).await;
10103    apply_additional_edits.await.unwrap();
10104}
10105
10106#[gpui::test]
10107async fn test_completion_replacing_suffix_in_multicursors(cx: &mut TestAppContext) {
10108    init_test(cx, |_| {});
10109    let mut cx = EditorLspTestContext::new_rust(
10110        lsp::ServerCapabilities {
10111            completion_provider: Some(lsp::CompletionOptions {
10112                resolve_provider: Some(true),
10113                ..Default::default()
10114            }),
10115            ..Default::default()
10116        },
10117        cx,
10118    )
10119    .await;
10120
10121    let initial_state = indoc! {"
10122        1. buf.to_offˇsuffix
10123        2. buf.to_offˇsuf
10124        3. buf.to_offˇfix
10125        4. buf.to_offˇ
10126        5. into_offˇensive
10127        6. ˇsuffix
10128        7. let ˇ //
10129        8. aaˇzz
10130        9. buf.to_off«zzzzzˇ»suffix
10131        10. buf.«ˇzzzzz»suffix
10132        11. to_off«ˇzzzzz»
10133
10134        buf.to_offˇsuffix  // newest cursor
10135    "};
10136    let completion_marked_buffer = indoc! {"
10137        1. buf.to_offsuffix
10138        2. buf.to_offsuf
10139        3. buf.to_offfix
10140        4. buf.to_off
10141        5. into_offensive
10142        6. suffix
10143        7. let  //
10144        8. aazz
10145        9. buf.to_offzzzzzsuffix
10146        10. buf.zzzzzsuffix
10147        11. to_offzzzzz
10148
10149        buf.<to_off|suffix>  // newest cursor
10150    "};
10151    let completion_text = "to_offset";
10152    let expected = indoc! {"
10153        1. buf.to_offsetˇ
10154        2. buf.to_offsetˇsuf
10155        3. buf.to_offsetˇfix
10156        4. buf.to_offsetˇ
10157        5. into_offsetˇensive
10158        6. to_offsetˇsuffix
10159        7. let to_offsetˇ //
10160        8. aato_offsetˇzz
10161        9. buf.to_offsetˇ
10162        10. buf.to_offsetˇsuffix
10163        11. to_offsetˇ
10164
10165        buf.to_offsetˇ  // newest cursor
10166    "};
10167
10168    cx.set_state(initial_state);
10169    cx.update_editor(|editor, window, cx| {
10170        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10171    });
10172
10173    let counter = Arc::new(AtomicUsize::new(0));
10174    handle_completion_request_with_insert_and_replace(
10175        &mut cx,
10176        completion_marked_buffer,
10177        vec![completion_text],
10178        counter.clone(),
10179    )
10180    .await;
10181    cx.condition(|editor, _| editor.context_menu_visible())
10182        .await;
10183    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10184
10185    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10186        editor
10187            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10188            .unwrap()
10189    });
10190    cx.assert_editor_state(expected);
10191    handle_resolve_completion_request(&mut cx, None).await;
10192    apply_additional_edits.await.unwrap();
10193}
10194
10195// This used to crash
10196#[gpui::test]
10197async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10198    init_test(cx, |_| {});
10199
10200    let buffer_text = indoc! {"
10201        fn main() {
10202            10.satu;
10203
10204            //
10205            // separate cursors so they open in different excerpts (manually reproducible)
10206            //
10207
10208            10.satu20;
10209        }
10210    "};
10211    let multibuffer_text_with_selections = indoc! {"
10212        fn main() {
10213            10.satuˇ;
10214
10215            //
10216
10217            //
10218
10219            10.satuˇ20;
10220        }
10221    "};
10222    let expected_multibuffer = indoc! {"
10223        fn main() {
10224            10.saturating_sub()ˇ;
10225
10226            //
10227
10228            //
10229
10230            10.saturating_sub()ˇ;
10231        }
10232    "};
10233
10234    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10235    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10236
10237    let fs = FakeFs::new(cx.executor());
10238    fs.insert_tree(
10239        path!("/a"),
10240        json!({
10241            "main.rs": buffer_text,
10242        }),
10243    )
10244    .await;
10245
10246    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10247    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10248    language_registry.add(rust_lang());
10249    let mut fake_servers = language_registry.register_fake_lsp(
10250        "Rust",
10251        FakeLspAdapter {
10252            capabilities: lsp::ServerCapabilities {
10253                completion_provider: Some(lsp::CompletionOptions {
10254                    resolve_provider: None,
10255                    ..lsp::CompletionOptions::default()
10256                }),
10257                ..lsp::ServerCapabilities::default()
10258            },
10259            ..FakeLspAdapter::default()
10260        },
10261    );
10262    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10263    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10264    let buffer = project
10265        .update(cx, |project, cx| {
10266            project.open_local_buffer(path!("/a/main.rs"), cx)
10267        })
10268        .await
10269        .unwrap();
10270
10271    let multi_buffer = cx.new(|cx| {
10272        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10273        multi_buffer.push_excerpts(
10274            buffer.clone(),
10275            [ExcerptRange::new(0..first_excerpt_end)],
10276            cx,
10277        );
10278        multi_buffer.push_excerpts(
10279            buffer.clone(),
10280            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10281            cx,
10282        );
10283        multi_buffer
10284    });
10285
10286    let editor = workspace
10287        .update(cx, |_, window, cx| {
10288            cx.new(|cx| {
10289                Editor::new(
10290                    EditorMode::Full {
10291                        scale_ui_elements_with_buffer_font_size: false,
10292                        show_active_line_background: false,
10293                    },
10294                    multi_buffer.clone(),
10295                    Some(project.clone()),
10296                    window,
10297                    cx,
10298                )
10299            })
10300        })
10301        .unwrap();
10302
10303    let pane = workspace
10304        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10305        .unwrap();
10306    pane.update_in(cx, |pane, window, cx| {
10307        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10308    });
10309
10310    let fake_server = fake_servers.next().await.unwrap();
10311
10312    editor.update_in(cx, |editor, window, cx| {
10313        editor.change_selections(None, window, cx, |s| {
10314            s.select_ranges([
10315                Point::new(1, 11)..Point::new(1, 11),
10316                Point::new(7, 11)..Point::new(7, 11),
10317            ])
10318        });
10319
10320        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10321    });
10322
10323    editor.update_in(cx, |editor, window, cx| {
10324        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10325    });
10326
10327    fake_server
10328        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10329            let completion_item = lsp::CompletionItem {
10330                label: "saturating_sub()".into(),
10331                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10332                    lsp::InsertReplaceEdit {
10333                        new_text: "saturating_sub()".to_owned(),
10334                        insert: lsp::Range::new(
10335                            lsp::Position::new(7, 7),
10336                            lsp::Position::new(7, 11),
10337                        ),
10338                        replace: lsp::Range::new(
10339                            lsp::Position::new(7, 7),
10340                            lsp::Position::new(7, 13),
10341                        ),
10342                    },
10343                )),
10344                ..lsp::CompletionItem::default()
10345            };
10346
10347            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10348        })
10349        .next()
10350        .await
10351        .unwrap();
10352
10353    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10354        .await;
10355
10356    editor
10357        .update_in(cx, |editor, window, cx| {
10358            editor
10359                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10360                .unwrap()
10361        })
10362        .await
10363        .unwrap();
10364
10365    editor.update(cx, |editor, cx| {
10366        assert_text_with_selections(editor, expected_multibuffer, cx);
10367    })
10368}
10369
10370#[gpui::test]
10371async fn test_completion(cx: &mut TestAppContext) {
10372    init_test(cx, |_| {});
10373
10374    let mut cx = EditorLspTestContext::new_rust(
10375        lsp::ServerCapabilities {
10376            completion_provider: Some(lsp::CompletionOptions {
10377                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10378                resolve_provider: Some(true),
10379                ..Default::default()
10380            }),
10381            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10382            ..Default::default()
10383        },
10384        cx,
10385    )
10386    .await;
10387    let counter = Arc::new(AtomicUsize::new(0));
10388
10389    cx.set_state(indoc! {"
10390        oneˇ
10391        two
10392        three
10393    "});
10394    cx.simulate_keystroke(".");
10395    handle_completion_request(
10396        &mut cx,
10397        indoc! {"
10398            one.|<>
10399            two
10400            three
10401        "},
10402        vec!["first_completion", "second_completion"],
10403        counter.clone(),
10404    )
10405    .await;
10406    cx.condition(|editor, _| editor.context_menu_visible())
10407        .await;
10408    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10409
10410    let _handler = handle_signature_help_request(
10411        &mut cx,
10412        lsp::SignatureHelp {
10413            signatures: vec![lsp::SignatureInformation {
10414                label: "test signature".to_string(),
10415                documentation: None,
10416                parameters: Some(vec![lsp::ParameterInformation {
10417                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10418                    documentation: None,
10419                }]),
10420                active_parameter: None,
10421            }],
10422            active_signature: None,
10423            active_parameter: None,
10424        },
10425    );
10426    cx.update_editor(|editor, window, cx| {
10427        assert!(
10428            !editor.signature_help_state.is_shown(),
10429            "No signature help was called for"
10430        );
10431        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10432    });
10433    cx.run_until_parked();
10434    cx.update_editor(|editor, _, _| {
10435        assert!(
10436            !editor.signature_help_state.is_shown(),
10437            "No signature help should be shown when completions menu is open"
10438        );
10439    });
10440
10441    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10442        editor.context_menu_next(&Default::default(), window, cx);
10443        editor
10444            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10445            .unwrap()
10446    });
10447    cx.assert_editor_state(indoc! {"
10448        one.second_completionˇ
10449        two
10450        three
10451    "});
10452
10453    handle_resolve_completion_request(
10454        &mut cx,
10455        Some(vec![
10456            (
10457                //This overlaps with the primary completion edit which is
10458                //misbehavior from the LSP spec, test that we filter it out
10459                indoc! {"
10460                    one.second_ˇcompletion
10461                    two
10462                    threeˇ
10463                "},
10464                "overlapping additional edit",
10465            ),
10466            (
10467                indoc! {"
10468                    one.second_completion
10469                    two
10470                    threeˇ
10471                "},
10472                "\nadditional edit",
10473            ),
10474        ]),
10475    )
10476    .await;
10477    apply_additional_edits.await.unwrap();
10478    cx.assert_editor_state(indoc! {"
10479        one.second_completionˇ
10480        two
10481        three
10482        additional edit
10483    "});
10484
10485    cx.set_state(indoc! {"
10486        one.second_completion
10487        twoˇ
10488        threeˇ
10489        additional edit
10490    "});
10491    cx.simulate_keystroke(" ");
10492    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10493    cx.simulate_keystroke("s");
10494    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10495
10496    cx.assert_editor_state(indoc! {"
10497        one.second_completion
10498        two sˇ
10499        three sˇ
10500        additional edit
10501    "});
10502    handle_completion_request(
10503        &mut cx,
10504        indoc! {"
10505            one.second_completion
10506            two s
10507            three <s|>
10508            additional edit
10509        "},
10510        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10511        counter.clone(),
10512    )
10513    .await;
10514    cx.condition(|editor, _| editor.context_menu_visible())
10515        .await;
10516    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10517
10518    cx.simulate_keystroke("i");
10519
10520    handle_completion_request(
10521        &mut cx,
10522        indoc! {"
10523            one.second_completion
10524            two si
10525            three <si|>
10526            additional edit
10527        "},
10528        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10529        counter.clone(),
10530    )
10531    .await;
10532    cx.condition(|editor, _| editor.context_menu_visible())
10533        .await;
10534    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10535
10536    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10537        editor
10538            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10539            .unwrap()
10540    });
10541    cx.assert_editor_state(indoc! {"
10542        one.second_completion
10543        two sixth_completionˇ
10544        three sixth_completionˇ
10545        additional edit
10546    "});
10547
10548    apply_additional_edits.await.unwrap();
10549
10550    update_test_language_settings(&mut cx, |settings| {
10551        settings.defaults.show_completions_on_input = Some(false);
10552    });
10553    cx.set_state("editorˇ");
10554    cx.simulate_keystroke(".");
10555    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10556    cx.simulate_keystrokes("c l o");
10557    cx.assert_editor_state("editor.cloˇ");
10558    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10559    cx.update_editor(|editor, window, cx| {
10560        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10561    });
10562    handle_completion_request(
10563        &mut cx,
10564        "editor.<clo|>",
10565        vec!["close", "clobber"],
10566        counter.clone(),
10567    )
10568    .await;
10569    cx.condition(|editor, _| editor.context_menu_visible())
10570        .await;
10571    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10572
10573    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10574        editor
10575            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10576            .unwrap()
10577    });
10578    cx.assert_editor_state("editor.closeˇ");
10579    handle_resolve_completion_request(&mut cx, None).await;
10580    apply_additional_edits.await.unwrap();
10581}
10582
10583#[gpui::test]
10584async fn test_word_completion(cx: &mut TestAppContext) {
10585    let lsp_fetch_timeout_ms = 10;
10586    init_test(cx, |language_settings| {
10587        language_settings.defaults.completions = Some(CompletionSettings {
10588            words: WordsCompletionMode::Fallback,
10589            lsp: true,
10590            lsp_fetch_timeout_ms: 10,
10591            lsp_insert_mode: LspInsertMode::Insert,
10592        });
10593    });
10594
10595    let mut cx = EditorLspTestContext::new_rust(
10596        lsp::ServerCapabilities {
10597            completion_provider: Some(lsp::CompletionOptions {
10598                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10599                ..lsp::CompletionOptions::default()
10600            }),
10601            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10602            ..lsp::ServerCapabilities::default()
10603        },
10604        cx,
10605    )
10606    .await;
10607
10608    let throttle_completions = Arc::new(AtomicBool::new(false));
10609
10610    let lsp_throttle_completions = throttle_completions.clone();
10611    let _completion_requests_handler =
10612        cx.lsp
10613            .server
10614            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10615                let lsp_throttle_completions = lsp_throttle_completions.clone();
10616                let cx = cx.clone();
10617                async move {
10618                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10619                        cx.background_executor()
10620                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10621                            .await;
10622                    }
10623                    Ok(Some(lsp::CompletionResponse::Array(vec![
10624                        lsp::CompletionItem {
10625                            label: "first".into(),
10626                            ..lsp::CompletionItem::default()
10627                        },
10628                        lsp::CompletionItem {
10629                            label: "last".into(),
10630                            ..lsp::CompletionItem::default()
10631                        },
10632                    ])))
10633                }
10634            });
10635
10636    cx.set_state(indoc! {"
10637        oneˇ
10638        two
10639        three
10640    "});
10641    cx.simulate_keystroke(".");
10642    cx.executor().run_until_parked();
10643    cx.condition(|editor, _| editor.context_menu_visible())
10644        .await;
10645    cx.update_editor(|editor, window, cx| {
10646        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10647        {
10648            assert_eq!(
10649                completion_menu_entries(&menu),
10650                &["first", "last"],
10651                "When LSP server is fast to reply, no fallback word completions are used"
10652            );
10653        } else {
10654            panic!("expected completion menu to be open");
10655        }
10656        editor.cancel(&Cancel, window, cx);
10657    });
10658    cx.executor().run_until_parked();
10659    cx.condition(|editor, _| !editor.context_menu_visible())
10660        .await;
10661
10662    throttle_completions.store(true, atomic::Ordering::Release);
10663    cx.simulate_keystroke(".");
10664    cx.executor()
10665        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10666    cx.executor().run_until_parked();
10667    cx.condition(|editor, _| editor.context_menu_visible())
10668        .await;
10669    cx.update_editor(|editor, _, _| {
10670        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10671        {
10672            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10673                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10674        } else {
10675            panic!("expected completion menu to be open");
10676        }
10677    });
10678}
10679
10680#[gpui::test]
10681async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10682    init_test(cx, |language_settings| {
10683        language_settings.defaults.completions = Some(CompletionSettings {
10684            words: WordsCompletionMode::Enabled,
10685            lsp: true,
10686            lsp_fetch_timeout_ms: 0,
10687            lsp_insert_mode: LspInsertMode::Insert,
10688        });
10689    });
10690
10691    let mut cx = EditorLspTestContext::new_rust(
10692        lsp::ServerCapabilities {
10693            completion_provider: Some(lsp::CompletionOptions {
10694                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10695                ..lsp::CompletionOptions::default()
10696            }),
10697            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10698            ..lsp::ServerCapabilities::default()
10699        },
10700        cx,
10701    )
10702    .await;
10703
10704    let _completion_requests_handler =
10705        cx.lsp
10706            .server
10707            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10708                Ok(Some(lsp::CompletionResponse::Array(vec![
10709                    lsp::CompletionItem {
10710                        label: "first".into(),
10711                        ..lsp::CompletionItem::default()
10712                    },
10713                    lsp::CompletionItem {
10714                        label: "last".into(),
10715                        ..lsp::CompletionItem::default()
10716                    },
10717                ])))
10718            });
10719
10720    cx.set_state(indoc! {"ˇ
10721        first
10722        last
10723        second
10724    "});
10725    cx.simulate_keystroke(".");
10726    cx.executor().run_until_parked();
10727    cx.condition(|editor, _| editor.context_menu_visible())
10728        .await;
10729    cx.update_editor(|editor, _, _| {
10730        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10731        {
10732            assert_eq!(
10733                completion_menu_entries(&menu),
10734                &["first", "last", "second"],
10735                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10736            );
10737        } else {
10738            panic!("expected completion menu to be open");
10739        }
10740    });
10741}
10742
10743#[gpui::test]
10744async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10745    init_test(cx, |language_settings| {
10746        language_settings.defaults.completions = Some(CompletionSettings {
10747            words: WordsCompletionMode::Disabled,
10748            lsp: true,
10749            lsp_fetch_timeout_ms: 0,
10750            lsp_insert_mode: LspInsertMode::Insert,
10751        });
10752    });
10753
10754    let mut cx = EditorLspTestContext::new_rust(
10755        lsp::ServerCapabilities {
10756            completion_provider: Some(lsp::CompletionOptions {
10757                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10758                ..lsp::CompletionOptions::default()
10759            }),
10760            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10761            ..lsp::ServerCapabilities::default()
10762        },
10763        cx,
10764    )
10765    .await;
10766
10767    let _completion_requests_handler =
10768        cx.lsp
10769            .server
10770            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10771                panic!("LSP completions should not be queried when dealing with word completions")
10772            });
10773
10774    cx.set_state(indoc! {"ˇ
10775        first
10776        last
10777        second
10778    "});
10779    cx.update_editor(|editor, window, cx| {
10780        editor.show_word_completions(&ShowWordCompletions, window, cx);
10781    });
10782    cx.executor().run_until_parked();
10783    cx.condition(|editor, _| editor.context_menu_visible())
10784        .await;
10785    cx.update_editor(|editor, _, _| {
10786        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10787        {
10788            assert_eq!(
10789                completion_menu_entries(&menu),
10790                &["first", "last", "second"],
10791                "`ShowWordCompletions` action should show word completions"
10792            );
10793        } else {
10794            panic!("expected completion menu to be open");
10795        }
10796    });
10797
10798    cx.simulate_keystroke("l");
10799    cx.executor().run_until_parked();
10800    cx.condition(|editor, _| editor.context_menu_visible())
10801        .await;
10802    cx.update_editor(|editor, _, _| {
10803        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10804        {
10805            assert_eq!(
10806                completion_menu_entries(&menu),
10807                &["last"],
10808                "After showing word completions, further editing should filter them and not query the LSP"
10809            );
10810        } else {
10811            panic!("expected completion menu to be open");
10812        }
10813    });
10814}
10815
10816#[gpui::test]
10817async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10818    init_test(cx, |language_settings| {
10819        language_settings.defaults.completions = Some(CompletionSettings {
10820            words: WordsCompletionMode::Fallback,
10821            lsp: false,
10822            lsp_fetch_timeout_ms: 0,
10823            lsp_insert_mode: LspInsertMode::Insert,
10824        });
10825    });
10826
10827    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10828
10829    cx.set_state(indoc! {"ˇ
10830        0_usize
10831        let
10832        33
10833        4.5f32
10834    "});
10835    cx.update_editor(|editor, window, cx| {
10836        editor.show_completions(&ShowCompletions::default(), window, cx);
10837    });
10838    cx.executor().run_until_parked();
10839    cx.condition(|editor, _| editor.context_menu_visible())
10840        .await;
10841    cx.update_editor(|editor, window, cx| {
10842        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10843        {
10844            assert_eq!(
10845                completion_menu_entries(&menu),
10846                &["let"],
10847                "With no digits in the completion query, no digits should be in the word completions"
10848            );
10849        } else {
10850            panic!("expected completion menu to be open");
10851        }
10852        editor.cancel(&Cancel, window, cx);
10853    });
10854
10855    cx.set_state(indoc! {"10856        0_usize
10857        let
10858        3
10859        33.35f32
10860    "});
10861    cx.update_editor(|editor, window, cx| {
10862        editor.show_completions(&ShowCompletions::default(), window, cx);
10863    });
10864    cx.executor().run_until_parked();
10865    cx.condition(|editor, _| editor.context_menu_visible())
10866        .await;
10867    cx.update_editor(|editor, _, _| {
10868        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10869        {
10870            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10871                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10872        } else {
10873            panic!("expected completion menu to be open");
10874        }
10875    });
10876}
10877
10878fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10879    let position = || lsp::Position {
10880        line: params.text_document_position.position.line,
10881        character: params.text_document_position.position.character,
10882    };
10883    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10884        range: lsp::Range {
10885            start: position(),
10886            end: position(),
10887        },
10888        new_text: text.to_string(),
10889    }))
10890}
10891
10892#[gpui::test]
10893async fn test_multiline_completion(cx: &mut TestAppContext) {
10894    init_test(cx, |_| {});
10895
10896    let fs = FakeFs::new(cx.executor());
10897    fs.insert_tree(
10898        path!("/a"),
10899        json!({
10900            "main.ts": "a",
10901        }),
10902    )
10903    .await;
10904
10905    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10906    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10907    let typescript_language = Arc::new(Language::new(
10908        LanguageConfig {
10909            name: "TypeScript".into(),
10910            matcher: LanguageMatcher {
10911                path_suffixes: vec!["ts".to_string()],
10912                ..LanguageMatcher::default()
10913            },
10914            line_comments: vec!["// ".into()],
10915            ..LanguageConfig::default()
10916        },
10917        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10918    ));
10919    language_registry.add(typescript_language.clone());
10920    let mut fake_servers = language_registry.register_fake_lsp(
10921        "TypeScript",
10922        FakeLspAdapter {
10923            capabilities: lsp::ServerCapabilities {
10924                completion_provider: Some(lsp::CompletionOptions {
10925                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10926                    ..lsp::CompletionOptions::default()
10927                }),
10928                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10929                ..lsp::ServerCapabilities::default()
10930            },
10931            // Emulate vtsls label generation
10932            label_for_completion: Some(Box::new(|item, _| {
10933                let text = if let Some(description) = item
10934                    .label_details
10935                    .as_ref()
10936                    .and_then(|label_details| label_details.description.as_ref())
10937                {
10938                    format!("{} {}", item.label, description)
10939                } else if let Some(detail) = &item.detail {
10940                    format!("{} {}", item.label, detail)
10941                } else {
10942                    item.label.clone()
10943                };
10944                let len = text.len();
10945                Some(language::CodeLabel {
10946                    text,
10947                    runs: Vec::new(),
10948                    filter_range: 0..len,
10949                })
10950            })),
10951            ..FakeLspAdapter::default()
10952        },
10953    );
10954    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10955    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10956    let worktree_id = workspace
10957        .update(cx, |workspace, _window, cx| {
10958            workspace.project().update(cx, |project, cx| {
10959                project.worktrees(cx).next().unwrap().read(cx).id()
10960            })
10961        })
10962        .unwrap();
10963    let _buffer = project
10964        .update(cx, |project, cx| {
10965            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10966        })
10967        .await
10968        .unwrap();
10969    let editor = workspace
10970        .update(cx, |workspace, window, cx| {
10971            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10972        })
10973        .unwrap()
10974        .await
10975        .unwrap()
10976        .downcast::<Editor>()
10977        .unwrap();
10978    let fake_server = fake_servers.next().await.unwrap();
10979
10980    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10981    let multiline_label_2 = "a\nb\nc\n";
10982    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10983    let multiline_description = "d\ne\nf\n";
10984    let multiline_detail_2 = "g\nh\ni\n";
10985
10986    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10987        move |params, _| async move {
10988            Ok(Some(lsp::CompletionResponse::Array(vec![
10989                lsp::CompletionItem {
10990                    label: multiline_label.to_string(),
10991                    text_edit: gen_text_edit(&params, "new_text_1"),
10992                    ..lsp::CompletionItem::default()
10993                },
10994                lsp::CompletionItem {
10995                    label: "single line label 1".to_string(),
10996                    detail: Some(multiline_detail.to_string()),
10997                    text_edit: gen_text_edit(&params, "new_text_2"),
10998                    ..lsp::CompletionItem::default()
10999                },
11000                lsp::CompletionItem {
11001                    label: "single line label 2".to_string(),
11002                    label_details: Some(lsp::CompletionItemLabelDetails {
11003                        description: Some(multiline_description.to_string()),
11004                        detail: None,
11005                    }),
11006                    text_edit: gen_text_edit(&params, "new_text_2"),
11007                    ..lsp::CompletionItem::default()
11008                },
11009                lsp::CompletionItem {
11010                    label: multiline_label_2.to_string(),
11011                    detail: Some(multiline_detail_2.to_string()),
11012                    text_edit: gen_text_edit(&params, "new_text_3"),
11013                    ..lsp::CompletionItem::default()
11014                },
11015                lsp::CompletionItem {
11016                    label: "Label with many     spaces and \t but without newlines".to_string(),
11017                    detail: Some(
11018                        "Details with many     spaces and \t but without newlines".to_string(),
11019                    ),
11020                    text_edit: gen_text_edit(&params, "new_text_4"),
11021                    ..lsp::CompletionItem::default()
11022                },
11023            ])))
11024        },
11025    );
11026
11027    editor.update_in(cx, |editor, window, cx| {
11028        cx.focus_self(window);
11029        editor.move_to_end(&MoveToEnd, window, cx);
11030        editor.handle_input(".", window, cx);
11031    });
11032    cx.run_until_parked();
11033    completion_handle.next().await.unwrap();
11034
11035    editor.update(cx, |editor, _| {
11036        assert!(editor.context_menu_visible());
11037        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11038        {
11039            let completion_labels = menu
11040                .completions
11041                .borrow()
11042                .iter()
11043                .map(|c| c.label.text.clone())
11044                .collect::<Vec<_>>();
11045            assert_eq!(
11046                completion_labels,
11047                &[
11048                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11049                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11050                    "single line label 2 d e f ",
11051                    "a b c g h i ",
11052                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11053                ],
11054                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11055            );
11056
11057            for completion in menu
11058                .completions
11059                .borrow()
11060                .iter() {
11061                    assert_eq!(
11062                        completion.label.filter_range,
11063                        0..completion.label.text.len(),
11064                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11065                    );
11066                }
11067        } else {
11068            panic!("expected completion menu to be open");
11069        }
11070    });
11071}
11072
11073#[gpui::test]
11074async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11075    init_test(cx, |_| {});
11076    let mut cx = EditorLspTestContext::new_rust(
11077        lsp::ServerCapabilities {
11078            completion_provider: Some(lsp::CompletionOptions {
11079                trigger_characters: Some(vec![".".to_string()]),
11080                ..Default::default()
11081            }),
11082            ..Default::default()
11083        },
11084        cx,
11085    )
11086    .await;
11087    cx.lsp
11088        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11089            Ok(Some(lsp::CompletionResponse::Array(vec![
11090                lsp::CompletionItem {
11091                    label: "first".into(),
11092                    ..Default::default()
11093                },
11094                lsp::CompletionItem {
11095                    label: "last".into(),
11096                    ..Default::default()
11097                },
11098            ])))
11099        });
11100    cx.set_state("variableˇ");
11101    cx.simulate_keystroke(".");
11102    cx.executor().run_until_parked();
11103
11104    cx.update_editor(|editor, _, _| {
11105        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11106        {
11107            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11108        } else {
11109            panic!("expected completion menu to be open");
11110        }
11111    });
11112
11113    cx.update_editor(|editor, window, cx| {
11114        editor.move_page_down(&MovePageDown::default(), window, cx);
11115        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11116        {
11117            assert!(
11118                menu.selected_item == 1,
11119                "expected PageDown to select the last item from the context menu"
11120            );
11121        } else {
11122            panic!("expected completion menu to stay open after PageDown");
11123        }
11124    });
11125
11126    cx.update_editor(|editor, window, cx| {
11127        editor.move_page_up(&MovePageUp::default(), window, cx);
11128        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11129        {
11130            assert!(
11131                menu.selected_item == 0,
11132                "expected PageUp to select the first item from the context menu"
11133            );
11134        } else {
11135            panic!("expected completion menu to stay open after PageUp");
11136        }
11137    });
11138}
11139
11140#[gpui::test]
11141async fn test_completion_sort(cx: &mut TestAppContext) {
11142    init_test(cx, |_| {});
11143    let mut cx = EditorLspTestContext::new_rust(
11144        lsp::ServerCapabilities {
11145            completion_provider: Some(lsp::CompletionOptions {
11146                trigger_characters: Some(vec![".".to_string()]),
11147                ..Default::default()
11148            }),
11149            ..Default::default()
11150        },
11151        cx,
11152    )
11153    .await;
11154    cx.lsp
11155        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11156            Ok(Some(lsp::CompletionResponse::Array(vec![
11157                lsp::CompletionItem {
11158                    label: "Range".into(),
11159                    sort_text: Some("a".into()),
11160                    ..Default::default()
11161                },
11162                lsp::CompletionItem {
11163                    label: "r".into(),
11164                    sort_text: Some("b".into()),
11165                    ..Default::default()
11166                },
11167                lsp::CompletionItem {
11168                    label: "ret".into(),
11169                    sort_text: Some("c".into()),
11170                    ..Default::default()
11171                },
11172                lsp::CompletionItem {
11173                    label: "return".into(),
11174                    sort_text: Some("d".into()),
11175                    ..Default::default()
11176                },
11177                lsp::CompletionItem {
11178                    label: "slice".into(),
11179                    sort_text: Some("d".into()),
11180                    ..Default::default()
11181                },
11182            ])))
11183        });
11184    cx.set_state("");
11185    cx.executor().run_until_parked();
11186    cx.update_editor(|editor, window, cx| {
11187        editor.show_completions(
11188            &ShowCompletions {
11189                trigger: Some("r".into()),
11190            },
11191            window,
11192            cx,
11193        );
11194    });
11195    cx.executor().run_until_parked();
11196
11197    cx.update_editor(|editor, _, _| {
11198        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11199        {
11200            assert_eq!(
11201                completion_menu_entries(&menu),
11202                &["r", "ret", "Range", "return"]
11203            );
11204        } else {
11205            panic!("expected completion menu to be open");
11206        }
11207    });
11208}
11209
11210#[gpui::test]
11211async fn test_as_is_completions(cx: &mut TestAppContext) {
11212    init_test(cx, |_| {});
11213    let mut cx = EditorLspTestContext::new_rust(
11214        lsp::ServerCapabilities {
11215            completion_provider: Some(lsp::CompletionOptions {
11216                ..Default::default()
11217            }),
11218            ..Default::default()
11219        },
11220        cx,
11221    )
11222    .await;
11223    cx.lsp
11224        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11225            Ok(Some(lsp::CompletionResponse::Array(vec![
11226                lsp::CompletionItem {
11227                    label: "unsafe".into(),
11228                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11229                        range: lsp::Range {
11230                            start: lsp::Position {
11231                                line: 1,
11232                                character: 2,
11233                            },
11234                            end: lsp::Position {
11235                                line: 1,
11236                                character: 3,
11237                            },
11238                        },
11239                        new_text: "unsafe".to_string(),
11240                    })),
11241                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11242                    ..Default::default()
11243                },
11244            ])))
11245        });
11246    cx.set_state("fn a() {}\n");
11247    cx.executor().run_until_parked();
11248    cx.update_editor(|editor, window, cx| {
11249        editor.show_completions(
11250            &ShowCompletions {
11251                trigger: Some("\n".into()),
11252            },
11253            window,
11254            cx,
11255        );
11256    });
11257    cx.executor().run_until_parked();
11258
11259    cx.update_editor(|editor, window, cx| {
11260        editor.confirm_completion(&Default::default(), window, cx)
11261    });
11262    cx.executor().run_until_parked();
11263    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11264}
11265
11266#[gpui::test]
11267async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11268    init_test(cx, |_| {});
11269
11270    let mut cx = EditorLspTestContext::new_rust(
11271        lsp::ServerCapabilities {
11272            completion_provider: Some(lsp::CompletionOptions {
11273                trigger_characters: Some(vec![".".to_string()]),
11274                resolve_provider: Some(true),
11275                ..Default::default()
11276            }),
11277            ..Default::default()
11278        },
11279        cx,
11280    )
11281    .await;
11282
11283    cx.set_state("fn main() { let a = 2ˇ; }");
11284    cx.simulate_keystroke(".");
11285    let completion_item = lsp::CompletionItem {
11286        label: "Some".into(),
11287        kind: Some(lsp::CompletionItemKind::SNIPPET),
11288        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11289        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11290            kind: lsp::MarkupKind::Markdown,
11291            value: "```rust\nSome(2)\n```".to_string(),
11292        })),
11293        deprecated: Some(false),
11294        sort_text: Some("Some".to_string()),
11295        filter_text: Some("Some".to_string()),
11296        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11297        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11298            range: lsp::Range {
11299                start: lsp::Position {
11300                    line: 0,
11301                    character: 22,
11302                },
11303                end: lsp::Position {
11304                    line: 0,
11305                    character: 22,
11306                },
11307            },
11308            new_text: "Some(2)".to_string(),
11309        })),
11310        additional_text_edits: Some(vec![lsp::TextEdit {
11311            range: lsp::Range {
11312                start: lsp::Position {
11313                    line: 0,
11314                    character: 20,
11315                },
11316                end: lsp::Position {
11317                    line: 0,
11318                    character: 22,
11319                },
11320            },
11321            new_text: "".to_string(),
11322        }]),
11323        ..Default::default()
11324    };
11325
11326    let closure_completion_item = completion_item.clone();
11327    let counter = Arc::new(AtomicUsize::new(0));
11328    let counter_clone = counter.clone();
11329    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11330        let task_completion_item = closure_completion_item.clone();
11331        counter_clone.fetch_add(1, atomic::Ordering::Release);
11332        async move {
11333            Ok(Some(lsp::CompletionResponse::Array(vec![
11334                task_completion_item,
11335            ])))
11336        }
11337    });
11338
11339    cx.condition(|editor, _| editor.context_menu_visible())
11340        .await;
11341    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11342    assert!(request.next().await.is_some());
11343    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11344
11345    cx.simulate_keystrokes("S o m");
11346    cx.condition(|editor, _| editor.context_menu_visible())
11347        .await;
11348    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11349    assert!(request.next().await.is_some());
11350    assert!(request.next().await.is_some());
11351    assert!(request.next().await.is_some());
11352    request.close();
11353    assert!(request.next().await.is_none());
11354    assert_eq!(
11355        counter.load(atomic::Ordering::Acquire),
11356        4,
11357        "With the completions menu open, only one LSP request should happen per input"
11358    );
11359}
11360
11361#[gpui::test]
11362async fn test_toggle_comment(cx: &mut TestAppContext) {
11363    init_test(cx, |_| {});
11364    let mut cx = EditorTestContext::new(cx).await;
11365    let language = Arc::new(Language::new(
11366        LanguageConfig {
11367            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11368            ..Default::default()
11369        },
11370        Some(tree_sitter_rust::LANGUAGE.into()),
11371    ));
11372    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11373
11374    // If multiple selections intersect a line, the line is only toggled once.
11375    cx.set_state(indoc! {"
11376        fn a() {
11377            «//b();
11378            ˇ»// «c();
11379            //ˇ»  d();
11380        }
11381    "});
11382
11383    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11384
11385    cx.assert_editor_state(indoc! {"
11386        fn a() {
11387            «b();
11388            c();
11389            ˇ» d();
11390        }
11391    "});
11392
11393    // The comment prefix is inserted at the same column for every line in a
11394    // selection.
11395    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11396
11397    cx.assert_editor_state(indoc! {"
11398        fn a() {
11399            // «b();
11400            // c();
11401            ˇ»//  d();
11402        }
11403    "});
11404
11405    // If a selection ends at the beginning of a line, that line is not toggled.
11406    cx.set_selections_state(indoc! {"
11407        fn a() {
11408            // b();
11409            «// c();
11410        ˇ»    //  d();
11411        }
11412    "});
11413
11414    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11415
11416    cx.assert_editor_state(indoc! {"
11417        fn a() {
11418            // b();
11419            «c();
11420        ˇ»    //  d();
11421        }
11422    "});
11423
11424    // If a selection span a single line and is empty, the line is toggled.
11425    cx.set_state(indoc! {"
11426        fn a() {
11427            a();
11428            b();
11429        ˇ
11430        }
11431    "});
11432
11433    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11434
11435    cx.assert_editor_state(indoc! {"
11436        fn a() {
11437            a();
11438            b();
11439        //•ˇ
11440        }
11441    "});
11442
11443    // If a selection span multiple lines, empty lines are not toggled.
11444    cx.set_state(indoc! {"
11445        fn a() {
11446            «a();
11447
11448            c();ˇ»
11449        }
11450    "});
11451
11452    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11453
11454    cx.assert_editor_state(indoc! {"
11455        fn a() {
11456            // «a();
11457
11458            // c();ˇ»
11459        }
11460    "});
11461
11462    // If a selection includes multiple comment prefixes, all lines are uncommented.
11463    cx.set_state(indoc! {"
11464        fn a() {
11465            «// a();
11466            /// b();
11467            //! c();ˇ»
11468        }
11469    "});
11470
11471    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11472
11473    cx.assert_editor_state(indoc! {"
11474        fn a() {
11475            «a();
11476            b();
11477            c();ˇ»
11478        }
11479    "});
11480}
11481
11482#[gpui::test]
11483async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11484    init_test(cx, |_| {});
11485    let mut cx = EditorTestContext::new(cx).await;
11486    let language = Arc::new(Language::new(
11487        LanguageConfig {
11488            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11489            ..Default::default()
11490        },
11491        Some(tree_sitter_rust::LANGUAGE.into()),
11492    ));
11493    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11494
11495    let toggle_comments = &ToggleComments {
11496        advance_downwards: false,
11497        ignore_indent: true,
11498    };
11499
11500    // If multiple selections intersect a line, the line is only toggled once.
11501    cx.set_state(indoc! {"
11502        fn a() {
11503        //    «b();
11504        //    c();
11505        //    ˇ» d();
11506        }
11507    "});
11508
11509    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11510
11511    cx.assert_editor_state(indoc! {"
11512        fn a() {
11513            «b();
11514            c();
11515            ˇ» d();
11516        }
11517    "});
11518
11519    // The comment prefix is inserted at the beginning of each line
11520    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11521
11522    cx.assert_editor_state(indoc! {"
11523        fn a() {
11524        //    «b();
11525        //    c();
11526        //    ˇ» d();
11527        }
11528    "});
11529
11530    // If a selection ends at the beginning of a line, that line is not toggled.
11531    cx.set_selections_state(indoc! {"
11532        fn a() {
11533        //    b();
11534        //    «c();
11535        ˇ»//     d();
11536        }
11537    "});
11538
11539    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11540
11541    cx.assert_editor_state(indoc! {"
11542        fn a() {
11543        //    b();
11544            «c();
11545        ˇ»//     d();
11546        }
11547    "});
11548
11549    // If a selection span a single line and is empty, the line is toggled.
11550    cx.set_state(indoc! {"
11551        fn a() {
11552            a();
11553            b();
11554        ˇ
11555        }
11556    "});
11557
11558    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11559
11560    cx.assert_editor_state(indoc! {"
11561        fn a() {
11562            a();
11563            b();
11564        //ˇ
11565        }
11566    "});
11567
11568    // If a selection span multiple lines, empty lines are not toggled.
11569    cx.set_state(indoc! {"
11570        fn a() {
11571            «a();
11572
11573            c();ˇ»
11574        }
11575    "});
11576
11577    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11578
11579    cx.assert_editor_state(indoc! {"
11580        fn a() {
11581        //    «a();
11582
11583        //    c();ˇ»
11584        }
11585    "});
11586
11587    // If a selection includes multiple comment prefixes, all lines are uncommented.
11588    cx.set_state(indoc! {"
11589        fn a() {
11590        //    «a();
11591        ///    b();
11592        //!    c();ˇ»
11593        }
11594    "});
11595
11596    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11597
11598    cx.assert_editor_state(indoc! {"
11599        fn a() {
11600            «a();
11601            b();
11602            c();ˇ»
11603        }
11604    "});
11605}
11606
11607#[gpui::test]
11608async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11609    init_test(cx, |_| {});
11610
11611    let language = Arc::new(Language::new(
11612        LanguageConfig {
11613            line_comments: vec!["// ".into()],
11614            ..Default::default()
11615        },
11616        Some(tree_sitter_rust::LANGUAGE.into()),
11617    ));
11618
11619    let mut cx = EditorTestContext::new(cx).await;
11620
11621    cx.language_registry().add(language.clone());
11622    cx.update_buffer(|buffer, cx| {
11623        buffer.set_language(Some(language), cx);
11624    });
11625
11626    let toggle_comments = &ToggleComments {
11627        advance_downwards: true,
11628        ignore_indent: false,
11629    };
11630
11631    // Single cursor on one line -> advance
11632    // Cursor moves horizontally 3 characters as well on non-blank line
11633    cx.set_state(indoc!(
11634        "fn a() {
11635             ˇdog();
11636             cat();
11637        }"
11638    ));
11639    cx.update_editor(|editor, window, cx| {
11640        editor.toggle_comments(toggle_comments, window, cx);
11641    });
11642    cx.assert_editor_state(indoc!(
11643        "fn a() {
11644             // dog();
11645             catˇ();
11646        }"
11647    ));
11648
11649    // Single selection on one line -> don't advance
11650    cx.set_state(indoc!(
11651        "fn a() {
11652             «dog()ˇ»;
11653             cat();
11654        }"
11655    ));
11656    cx.update_editor(|editor, window, cx| {
11657        editor.toggle_comments(toggle_comments, window, cx);
11658    });
11659    cx.assert_editor_state(indoc!(
11660        "fn a() {
11661             // «dog()ˇ»;
11662             cat();
11663        }"
11664    ));
11665
11666    // Multiple cursors on one line -> advance
11667    cx.set_state(indoc!(
11668        "fn a() {
11669             ˇdˇog();
11670             cat();
11671        }"
11672    ));
11673    cx.update_editor(|editor, window, cx| {
11674        editor.toggle_comments(toggle_comments, window, cx);
11675    });
11676    cx.assert_editor_state(indoc!(
11677        "fn a() {
11678             // dog();
11679             catˇ(ˇ);
11680        }"
11681    ));
11682
11683    // Multiple cursors on one line, with selection -> don't advance
11684    cx.set_state(indoc!(
11685        "fn a() {
11686             ˇdˇog«()ˇ»;
11687             cat();
11688        }"
11689    ));
11690    cx.update_editor(|editor, window, cx| {
11691        editor.toggle_comments(toggle_comments, window, cx);
11692    });
11693    cx.assert_editor_state(indoc!(
11694        "fn a() {
11695             // ˇdˇog«()ˇ»;
11696             cat();
11697        }"
11698    ));
11699
11700    // Single cursor on one line -> advance
11701    // Cursor moves to column 0 on blank line
11702    cx.set_state(indoc!(
11703        "fn a() {
11704             ˇdog();
11705
11706             cat();
11707        }"
11708    ));
11709    cx.update_editor(|editor, window, cx| {
11710        editor.toggle_comments(toggle_comments, window, cx);
11711    });
11712    cx.assert_editor_state(indoc!(
11713        "fn a() {
11714             // dog();
11715        ˇ
11716             cat();
11717        }"
11718    ));
11719
11720    // Single cursor on one line -> advance
11721    // Cursor starts and ends at column 0
11722    cx.set_state(indoc!(
11723        "fn a() {
11724         ˇ    dog();
11725             cat();
11726        }"
11727    ));
11728    cx.update_editor(|editor, window, cx| {
11729        editor.toggle_comments(toggle_comments, window, cx);
11730    });
11731    cx.assert_editor_state(indoc!(
11732        "fn a() {
11733             // dog();
11734         ˇ    cat();
11735        }"
11736    ));
11737}
11738
11739#[gpui::test]
11740async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11741    init_test(cx, |_| {});
11742
11743    let mut cx = EditorTestContext::new(cx).await;
11744
11745    let html_language = Arc::new(
11746        Language::new(
11747            LanguageConfig {
11748                name: "HTML".into(),
11749                block_comment: Some(("<!-- ".into(), " -->".into())),
11750                ..Default::default()
11751            },
11752            Some(tree_sitter_html::LANGUAGE.into()),
11753        )
11754        .with_injection_query(
11755            r#"
11756            (script_element
11757                (raw_text) @injection.content
11758                (#set! injection.language "javascript"))
11759            "#,
11760        )
11761        .unwrap(),
11762    );
11763
11764    let javascript_language = Arc::new(Language::new(
11765        LanguageConfig {
11766            name: "JavaScript".into(),
11767            line_comments: vec!["// ".into()],
11768            ..Default::default()
11769        },
11770        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11771    ));
11772
11773    cx.language_registry().add(html_language.clone());
11774    cx.language_registry().add(javascript_language.clone());
11775    cx.update_buffer(|buffer, cx| {
11776        buffer.set_language(Some(html_language), cx);
11777    });
11778
11779    // Toggle comments for empty selections
11780    cx.set_state(
11781        &r#"
11782            <p>A</p>ˇ
11783            <p>B</p>ˇ
11784            <p>C</p>ˇ
11785        "#
11786        .unindent(),
11787    );
11788    cx.update_editor(|editor, window, cx| {
11789        editor.toggle_comments(&ToggleComments::default(), window, cx)
11790    });
11791    cx.assert_editor_state(
11792        &r#"
11793            <!-- <p>A</p>ˇ -->
11794            <!-- <p>B</p>ˇ -->
11795            <!-- <p>C</p>ˇ -->
11796        "#
11797        .unindent(),
11798    );
11799    cx.update_editor(|editor, window, cx| {
11800        editor.toggle_comments(&ToggleComments::default(), window, cx)
11801    });
11802    cx.assert_editor_state(
11803        &r#"
11804            <p>A</p>ˇ
11805            <p>B</p>ˇ
11806            <p>C</p>ˇ
11807        "#
11808        .unindent(),
11809    );
11810
11811    // Toggle comments for mixture of empty and non-empty selections, where
11812    // multiple selections occupy a given line.
11813    cx.set_state(
11814        &r#"
11815            <p>A«</p>
11816            <p>ˇ»B</p>ˇ
11817            <p>C«</p>
11818            <p>ˇ»D</p>ˇ
11819        "#
11820        .unindent(),
11821    );
11822
11823    cx.update_editor(|editor, window, cx| {
11824        editor.toggle_comments(&ToggleComments::default(), window, cx)
11825    });
11826    cx.assert_editor_state(
11827        &r#"
11828            <!-- <p>A«</p>
11829            <p>ˇ»B</p>ˇ -->
11830            <!-- <p>C«</p>
11831            <p>ˇ»D</p>ˇ -->
11832        "#
11833        .unindent(),
11834    );
11835    cx.update_editor(|editor, window, cx| {
11836        editor.toggle_comments(&ToggleComments::default(), window, cx)
11837    });
11838    cx.assert_editor_state(
11839        &r#"
11840            <p>A«</p>
11841            <p>ˇ»B</p>ˇ
11842            <p>C«</p>
11843            <p>ˇ»D</p>ˇ
11844        "#
11845        .unindent(),
11846    );
11847
11848    // Toggle comments when different languages are active for different
11849    // selections.
11850    cx.set_state(
11851        &r#"
11852            ˇ<script>
11853                ˇvar x = new Y();
11854            ˇ</script>
11855        "#
11856        .unindent(),
11857    );
11858    cx.executor().run_until_parked();
11859    cx.update_editor(|editor, window, cx| {
11860        editor.toggle_comments(&ToggleComments::default(), window, cx)
11861    });
11862    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11863    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11864    cx.assert_editor_state(
11865        &r#"
11866            <!-- ˇ<script> -->
11867                // ˇvar x = new Y();
11868            <!-- ˇ</script> -->
11869        "#
11870        .unindent(),
11871    );
11872}
11873
11874#[gpui::test]
11875fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11876    init_test(cx, |_| {});
11877
11878    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11879    let multibuffer = cx.new(|cx| {
11880        let mut multibuffer = MultiBuffer::new(ReadWrite);
11881        multibuffer.push_excerpts(
11882            buffer.clone(),
11883            [
11884                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11885                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11886            ],
11887            cx,
11888        );
11889        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11890        multibuffer
11891    });
11892
11893    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11894    editor.update_in(cx, |editor, window, cx| {
11895        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11896        editor.change_selections(None, window, cx, |s| {
11897            s.select_ranges([
11898                Point::new(0, 0)..Point::new(0, 0),
11899                Point::new(1, 0)..Point::new(1, 0),
11900            ])
11901        });
11902
11903        editor.handle_input("X", window, cx);
11904        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11905        assert_eq!(
11906            editor.selections.ranges(cx),
11907            [
11908                Point::new(0, 1)..Point::new(0, 1),
11909                Point::new(1, 1)..Point::new(1, 1),
11910            ]
11911        );
11912
11913        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11914        editor.change_selections(None, window, cx, |s| {
11915            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11916        });
11917        editor.backspace(&Default::default(), window, cx);
11918        assert_eq!(editor.text(cx), "Xa\nbbb");
11919        assert_eq!(
11920            editor.selections.ranges(cx),
11921            [Point::new(1, 0)..Point::new(1, 0)]
11922        );
11923
11924        editor.change_selections(None, window, cx, |s| {
11925            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11926        });
11927        editor.backspace(&Default::default(), window, cx);
11928        assert_eq!(editor.text(cx), "X\nbb");
11929        assert_eq!(
11930            editor.selections.ranges(cx),
11931            [Point::new(0, 1)..Point::new(0, 1)]
11932        );
11933    });
11934}
11935
11936#[gpui::test]
11937fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11938    init_test(cx, |_| {});
11939
11940    let markers = vec![('[', ']').into(), ('(', ')').into()];
11941    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11942        indoc! {"
11943            [aaaa
11944            (bbbb]
11945            cccc)",
11946        },
11947        markers.clone(),
11948    );
11949    let excerpt_ranges = markers.into_iter().map(|marker| {
11950        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11951        ExcerptRange::new(context.clone())
11952    });
11953    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11954    let multibuffer = cx.new(|cx| {
11955        let mut multibuffer = MultiBuffer::new(ReadWrite);
11956        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11957        multibuffer
11958    });
11959
11960    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11961    editor.update_in(cx, |editor, window, cx| {
11962        let (expected_text, selection_ranges) = marked_text_ranges(
11963            indoc! {"
11964                aaaa
11965                bˇbbb
11966                bˇbbˇb
11967                cccc"
11968            },
11969            true,
11970        );
11971        assert_eq!(editor.text(cx), expected_text);
11972        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11973
11974        editor.handle_input("X", window, cx);
11975
11976        let (expected_text, expected_selections) = marked_text_ranges(
11977            indoc! {"
11978                aaaa
11979                bXˇbbXb
11980                bXˇbbXˇb
11981                cccc"
11982            },
11983            false,
11984        );
11985        assert_eq!(editor.text(cx), expected_text);
11986        assert_eq!(editor.selections.ranges(cx), expected_selections);
11987
11988        editor.newline(&Newline, window, cx);
11989        let (expected_text, expected_selections) = marked_text_ranges(
11990            indoc! {"
11991                aaaa
11992                bX
11993                ˇbbX
11994                b
11995                bX
11996                ˇbbX
11997                ˇb
11998                cccc"
11999            },
12000            false,
12001        );
12002        assert_eq!(editor.text(cx), expected_text);
12003        assert_eq!(editor.selections.ranges(cx), expected_selections);
12004    });
12005}
12006
12007#[gpui::test]
12008fn test_refresh_selections(cx: &mut TestAppContext) {
12009    init_test(cx, |_| {});
12010
12011    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12012    let mut excerpt1_id = None;
12013    let multibuffer = cx.new(|cx| {
12014        let mut multibuffer = MultiBuffer::new(ReadWrite);
12015        excerpt1_id = multibuffer
12016            .push_excerpts(
12017                buffer.clone(),
12018                [
12019                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12020                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12021                ],
12022                cx,
12023            )
12024            .into_iter()
12025            .next();
12026        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12027        multibuffer
12028    });
12029
12030    let editor = cx.add_window(|window, cx| {
12031        let mut editor = build_editor(multibuffer.clone(), window, cx);
12032        let snapshot = editor.snapshot(window, cx);
12033        editor.change_selections(None, window, cx, |s| {
12034            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12035        });
12036        editor.begin_selection(
12037            Point::new(2, 1).to_display_point(&snapshot),
12038            true,
12039            1,
12040            window,
12041            cx,
12042        );
12043        assert_eq!(
12044            editor.selections.ranges(cx),
12045            [
12046                Point::new(1, 3)..Point::new(1, 3),
12047                Point::new(2, 1)..Point::new(2, 1),
12048            ]
12049        );
12050        editor
12051    });
12052
12053    // Refreshing selections is a no-op when excerpts haven't changed.
12054    _ = editor.update(cx, |editor, window, cx| {
12055        editor.change_selections(None, window, cx, |s| s.refresh());
12056        assert_eq!(
12057            editor.selections.ranges(cx),
12058            [
12059                Point::new(1, 3)..Point::new(1, 3),
12060                Point::new(2, 1)..Point::new(2, 1),
12061            ]
12062        );
12063    });
12064
12065    multibuffer.update(cx, |multibuffer, cx| {
12066        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12067    });
12068    _ = editor.update(cx, |editor, window, cx| {
12069        // Removing an excerpt causes the first selection to become degenerate.
12070        assert_eq!(
12071            editor.selections.ranges(cx),
12072            [
12073                Point::new(0, 0)..Point::new(0, 0),
12074                Point::new(0, 1)..Point::new(0, 1)
12075            ]
12076        );
12077
12078        // Refreshing selections will relocate the first selection to the original buffer
12079        // location.
12080        editor.change_selections(None, window, cx, |s| s.refresh());
12081        assert_eq!(
12082            editor.selections.ranges(cx),
12083            [
12084                Point::new(0, 1)..Point::new(0, 1),
12085                Point::new(0, 3)..Point::new(0, 3)
12086            ]
12087        );
12088        assert!(editor.selections.pending_anchor().is_some());
12089    });
12090}
12091
12092#[gpui::test]
12093fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12094    init_test(cx, |_| {});
12095
12096    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12097    let mut excerpt1_id = None;
12098    let multibuffer = cx.new(|cx| {
12099        let mut multibuffer = MultiBuffer::new(ReadWrite);
12100        excerpt1_id = multibuffer
12101            .push_excerpts(
12102                buffer.clone(),
12103                [
12104                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12105                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12106                ],
12107                cx,
12108            )
12109            .into_iter()
12110            .next();
12111        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12112        multibuffer
12113    });
12114
12115    let editor = cx.add_window(|window, cx| {
12116        let mut editor = build_editor(multibuffer.clone(), window, cx);
12117        let snapshot = editor.snapshot(window, cx);
12118        editor.begin_selection(
12119            Point::new(1, 3).to_display_point(&snapshot),
12120            false,
12121            1,
12122            window,
12123            cx,
12124        );
12125        assert_eq!(
12126            editor.selections.ranges(cx),
12127            [Point::new(1, 3)..Point::new(1, 3)]
12128        );
12129        editor
12130    });
12131
12132    multibuffer.update(cx, |multibuffer, cx| {
12133        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12134    });
12135    _ = editor.update(cx, |editor, window, cx| {
12136        assert_eq!(
12137            editor.selections.ranges(cx),
12138            [Point::new(0, 0)..Point::new(0, 0)]
12139        );
12140
12141        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12142        editor.change_selections(None, window, cx, |s| s.refresh());
12143        assert_eq!(
12144            editor.selections.ranges(cx),
12145            [Point::new(0, 3)..Point::new(0, 3)]
12146        );
12147        assert!(editor.selections.pending_anchor().is_some());
12148    });
12149}
12150
12151#[gpui::test]
12152async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12153    init_test(cx, |_| {});
12154
12155    let language = Arc::new(
12156        Language::new(
12157            LanguageConfig {
12158                brackets: BracketPairConfig {
12159                    pairs: vec![
12160                        BracketPair {
12161                            start: "{".to_string(),
12162                            end: "}".to_string(),
12163                            close: true,
12164                            surround: true,
12165                            newline: true,
12166                        },
12167                        BracketPair {
12168                            start: "/* ".to_string(),
12169                            end: " */".to_string(),
12170                            close: true,
12171                            surround: true,
12172                            newline: true,
12173                        },
12174                    ],
12175                    ..Default::default()
12176                },
12177                ..Default::default()
12178            },
12179            Some(tree_sitter_rust::LANGUAGE.into()),
12180        )
12181        .with_indents_query("")
12182        .unwrap(),
12183    );
12184
12185    let text = concat!(
12186        "{   }\n",     //
12187        "  x\n",       //
12188        "  /*   */\n", //
12189        "x\n",         //
12190        "{{} }\n",     //
12191    );
12192
12193    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12194    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12195    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12196    editor
12197        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12198        .await;
12199
12200    editor.update_in(cx, |editor, window, cx| {
12201        editor.change_selections(None, window, cx, |s| {
12202            s.select_display_ranges([
12203                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12204                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12205                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12206            ])
12207        });
12208        editor.newline(&Newline, window, cx);
12209
12210        assert_eq!(
12211            editor.buffer().read(cx).read(cx).text(),
12212            concat!(
12213                "{ \n",    // Suppress rustfmt
12214                "\n",      //
12215                "}\n",     //
12216                "  x\n",   //
12217                "  /* \n", //
12218                "  \n",    //
12219                "  */\n",  //
12220                "x\n",     //
12221                "{{} \n",  //
12222                "}\n",     //
12223            )
12224        );
12225    });
12226}
12227
12228#[gpui::test]
12229fn test_highlighted_ranges(cx: &mut TestAppContext) {
12230    init_test(cx, |_| {});
12231
12232    let editor = cx.add_window(|window, cx| {
12233        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12234        build_editor(buffer.clone(), window, cx)
12235    });
12236
12237    _ = editor.update(cx, |editor, window, cx| {
12238        struct Type1;
12239        struct Type2;
12240
12241        let buffer = editor.buffer.read(cx).snapshot(cx);
12242
12243        let anchor_range =
12244            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12245
12246        editor.highlight_background::<Type1>(
12247            &[
12248                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12249                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12250                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12251                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12252            ],
12253            |_| Hsla::red(),
12254            cx,
12255        );
12256        editor.highlight_background::<Type2>(
12257            &[
12258                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12259                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12260                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12261                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12262            ],
12263            |_| Hsla::green(),
12264            cx,
12265        );
12266
12267        let snapshot = editor.snapshot(window, cx);
12268        let mut highlighted_ranges = editor.background_highlights_in_range(
12269            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12270            &snapshot,
12271            cx.theme().colors(),
12272        );
12273        // Enforce a consistent ordering based on color without relying on the ordering of the
12274        // highlight's `TypeId` which is non-executor.
12275        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12276        assert_eq!(
12277            highlighted_ranges,
12278            &[
12279                (
12280                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12281                    Hsla::red(),
12282                ),
12283                (
12284                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12285                    Hsla::red(),
12286                ),
12287                (
12288                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12289                    Hsla::green(),
12290                ),
12291                (
12292                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12293                    Hsla::green(),
12294                ),
12295            ]
12296        );
12297        assert_eq!(
12298            editor.background_highlights_in_range(
12299                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12300                &snapshot,
12301                cx.theme().colors(),
12302            ),
12303            &[(
12304                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12305                Hsla::red(),
12306            )]
12307        );
12308    });
12309}
12310
12311#[gpui::test]
12312async fn test_following(cx: &mut TestAppContext) {
12313    init_test(cx, |_| {});
12314
12315    let fs = FakeFs::new(cx.executor());
12316    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12317
12318    let buffer = project.update(cx, |project, cx| {
12319        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12320        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12321    });
12322    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12323    let follower = cx.update(|cx| {
12324        cx.open_window(
12325            WindowOptions {
12326                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12327                    gpui::Point::new(px(0.), px(0.)),
12328                    gpui::Point::new(px(10.), px(80.)),
12329                ))),
12330                ..Default::default()
12331            },
12332            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12333        )
12334        .unwrap()
12335    });
12336
12337    let is_still_following = Rc::new(RefCell::new(true));
12338    let follower_edit_event_count = Rc::new(RefCell::new(0));
12339    let pending_update = Rc::new(RefCell::new(None));
12340    let leader_entity = leader.root(cx).unwrap();
12341    let follower_entity = follower.root(cx).unwrap();
12342    _ = follower.update(cx, {
12343        let update = pending_update.clone();
12344        let is_still_following = is_still_following.clone();
12345        let follower_edit_event_count = follower_edit_event_count.clone();
12346        |_, window, cx| {
12347            cx.subscribe_in(
12348                &leader_entity,
12349                window,
12350                move |_, leader, event, window, cx| {
12351                    leader.read(cx).add_event_to_update_proto(
12352                        event,
12353                        &mut update.borrow_mut(),
12354                        window,
12355                        cx,
12356                    );
12357                },
12358            )
12359            .detach();
12360
12361            cx.subscribe_in(
12362                &follower_entity,
12363                window,
12364                move |_, _, event: &EditorEvent, _window, _cx| {
12365                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12366                        *is_still_following.borrow_mut() = false;
12367                    }
12368
12369                    if let EditorEvent::BufferEdited = event {
12370                        *follower_edit_event_count.borrow_mut() += 1;
12371                    }
12372                },
12373            )
12374            .detach();
12375        }
12376    });
12377
12378    // Update the selections only
12379    _ = leader.update(cx, |leader, window, cx| {
12380        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12381    });
12382    follower
12383        .update(cx, |follower, window, cx| {
12384            follower.apply_update_proto(
12385                &project,
12386                pending_update.borrow_mut().take().unwrap(),
12387                window,
12388                cx,
12389            )
12390        })
12391        .unwrap()
12392        .await
12393        .unwrap();
12394    _ = follower.update(cx, |follower, _, cx| {
12395        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12396    });
12397    assert!(*is_still_following.borrow());
12398    assert_eq!(*follower_edit_event_count.borrow(), 0);
12399
12400    // Update the scroll position only
12401    _ = leader.update(cx, |leader, window, cx| {
12402        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12403    });
12404    follower
12405        .update(cx, |follower, window, cx| {
12406            follower.apply_update_proto(
12407                &project,
12408                pending_update.borrow_mut().take().unwrap(),
12409                window,
12410                cx,
12411            )
12412        })
12413        .unwrap()
12414        .await
12415        .unwrap();
12416    assert_eq!(
12417        follower
12418            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12419            .unwrap(),
12420        gpui::Point::new(1.5, 3.5)
12421    );
12422    assert!(*is_still_following.borrow());
12423    assert_eq!(*follower_edit_event_count.borrow(), 0);
12424
12425    // Update the selections and scroll position. The follower's scroll position is updated
12426    // via autoscroll, not via the leader's exact scroll position.
12427    _ = leader.update(cx, |leader, window, cx| {
12428        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12429        leader.request_autoscroll(Autoscroll::newest(), cx);
12430        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12431    });
12432    follower
12433        .update(cx, |follower, window, cx| {
12434            follower.apply_update_proto(
12435                &project,
12436                pending_update.borrow_mut().take().unwrap(),
12437                window,
12438                cx,
12439            )
12440        })
12441        .unwrap()
12442        .await
12443        .unwrap();
12444    _ = follower.update(cx, |follower, _, cx| {
12445        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12446        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12447    });
12448    assert!(*is_still_following.borrow());
12449
12450    // Creating a pending selection that precedes another selection
12451    _ = leader.update(cx, |leader, window, cx| {
12452        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12453        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12454    });
12455    follower
12456        .update(cx, |follower, window, cx| {
12457            follower.apply_update_proto(
12458                &project,
12459                pending_update.borrow_mut().take().unwrap(),
12460                window,
12461                cx,
12462            )
12463        })
12464        .unwrap()
12465        .await
12466        .unwrap();
12467    _ = follower.update(cx, |follower, _, cx| {
12468        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12469    });
12470    assert!(*is_still_following.borrow());
12471
12472    // Extend the pending selection so that it surrounds another selection
12473    _ = leader.update(cx, |leader, window, cx| {
12474        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12475    });
12476    follower
12477        .update(cx, |follower, window, cx| {
12478            follower.apply_update_proto(
12479                &project,
12480                pending_update.borrow_mut().take().unwrap(),
12481                window,
12482                cx,
12483            )
12484        })
12485        .unwrap()
12486        .await
12487        .unwrap();
12488    _ = follower.update(cx, |follower, _, cx| {
12489        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12490    });
12491
12492    // Scrolling locally breaks the follow
12493    _ = follower.update(cx, |follower, window, cx| {
12494        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12495        follower.set_scroll_anchor(
12496            ScrollAnchor {
12497                anchor: top_anchor,
12498                offset: gpui::Point::new(0.0, 0.5),
12499            },
12500            window,
12501            cx,
12502        );
12503    });
12504    assert!(!(*is_still_following.borrow()));
12505}
12506
12507#[gpui::test]
12508async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12509    init_test(cx, |_| {});
12510
12511    let fs = FakeFs::new(cx.executor());
12512    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12513    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12514    let pane = workspace
12515        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12516        .unwrap();
12517
12518    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12519
12520    let leader = pane.update_in(cx, |_, window, cx| {
12521        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12522        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12523    });
12524
12525    // Start following the editor when it has no excerpts.
12526    let mut state_message =
12527        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12528    let workspace_entity = workspace.root(cx).unwrap();
12529    let follower_1 = cx
12530        .update_window(*workspace.deref(), |_, window, cx| {
12531            Editor::from_state_proto(
12532                workspace_entity,
12533                ViewId {
12534                    creator: Default::default(),
12535                    id: 0,
12536                },
12537                &mut state_message,
12538                window,
12539                cx,
12540            )
12541        })
12542        .unwrap()
12543        .unwrap()
12544        .await
12545        .unwrap();
12546
12547    let update_message = Rc::new(RefCell::new(None));
12548    follower_1.update_in(cx, {
12549        let update = update_message.clone();
12550        |_, window, cx| {
12551            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12552                leader.read(cx).add_event_to_update_proto(
12553                    event,
12554                    &mut update.borrow_mut(),
12555                    window,
12556                    cx,
12557                );
12558            })
12559            .detach();
12560        }
12561    });
12562
12563    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12564        (
12565            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12566            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12567        )
12568    });
12569
12570    // Insert some excerpts.
12571    leader.update(cx, |leader, cx| {
12572        leader.buffer.update(cx, |multibuffer, cx| {
12573            let excerpt_ids = multibuffer.push_excerpts(
12574                buffer_1.clone(),
12575                [
12576                    ExcerptRange::new(1..6),
12577                    ExcerptRange::new(12..15),
12578                    ExcerptRange::new(0..3),
12579                ],
12580                cx,
12581            );
12582            multibuffer.insert_excerpts_after(
12583                excerpt_ids[0],
12584                buffer_2.clone(),
12585                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
12586                cx,
12587            );
12588        });
12589    });
12590
12591    // Apply the update of adding the excerpts.
12592    follower_1
12593        .update_in(cx, |follower, window, cx| {
12594            follower.apply_update_proto(
12595                &project,
12596                update_message.borrow().clone().unwrap(),
12597                window,
12598                cx,
12599            )
12600        })
12601        .await
12602        .unwrap();
12603    assert_eq!(
12604        follower_1.update(cx, |editor, cx| editor.text(cx)),
12605        leader.update(cx, |editor, cx| editor.text(cx))
12606    );
12607    update_message.borrow_mut().take();
12608
12609    // Start following separately after it already has excerpts.
12610    let mut state_message =
12611        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12612    let workspace_entity = workspace.root(cx).unwrap();
12613    let follower_2 = cx
12614        .update_window(*workspace.deref(), |_, window, cx| {
12615            Editor::from_state_proto(
12616                workspace_entity,
12617                ViewId {
12618                    creator: Default::default(),
12619                    id: 0,
12620                },
12621                &mut state_message,
12622                window,
12623                cx,
12624            )
12625        })
12626        .unwrap()
12627        .unwrap()
12628        .await
12629        .unwrap();
12630    assert_eq!(
12631        follower_2.update(cx, |editor, cx| editor.text(cx)),
12632        leader.update(cx, |editor, cx| editor.text(cx))
12633    );
12634
12635    // Remove some excerpts.
12636    leader.update(cx, |leader, cx| {
12637        leader.buffer.update(cx, |multibuffer, cx| {
12638            let excerpt_ids = multibuffer.excerpt_ids();
12639            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12640            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12641        });
12642    });
12643
12644    // Apply the update of removing the excerpts.
12645    follower_1
12646        .update_in(cx, |follower, window, cx| {
12647            follower.apply_update_proto(
12648                &project,
12649                update_message.borrow().clone().unwrap(),
12650                window,
12651                cx,
12652            )
12653        })
12654        .await
12655        .unwrap();
12656    follower_2
12657        .update_in(cx, |follower, window, cx| {
12658            follower.apply_update_proto(
12659                &project,
12660                update_message.borrow().clone().unwrap(),
12661                window,
12662                cx,
12663            )
12664        })
12665        .await
12666        .unwrap();
12667    update_message.borrow_mut().take();
12668    assert_eq!(
12669        follower_1.update(cx, |editor, cx| editor.text(cx)),
12670        leader.update(cx, |editor, cx| editor.text(cx))
12671    );
12672}
12673
12674#[gpui::test]
12675async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12676    init_test(cx, |_| {});
12677
12678    let mut cx = EditorTestContext::new(cx).await;
12679    let lsp_store =
12680        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12681
12682    cx.set_state(indoc! {"
12683        ˇfn func(abc def: i32) -> u32 {
12684        }
12685    "});
12686
12687    cx.update(|_, cx| {
12688        lsp_store.update(cx, |lsp_store, cx| {
12689            lsp_store
12690                .update_diagnostics(
12691                    LanguageServerId(0),
12692                    lsp::PublishDiagnosticsParams {
12693                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12694                        version: None,
12695                        diagnostics: vec![
12696                            lsp::Diagnostic {
12697                                range: lsp::Range::new(
12698                                    lsp::Position::new(0, 11),
12699                                    lsp::Position::new(0, 12),
12700                                ),
12701                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12702                                ..Default::default()
12703                            },
12704                            lsp::Diagnostic {
12705                                range: lsp::Range::new(
12706                                    lsp::Position::new(0, 12),
12707                                    lsp::Position::new(0, 15),
12708                                ),
12709                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12710                                ..Default::default()
12711                            },
12712                            lsp::Diagnostic {
12713                                range: lsp::Range::new(
12714                                    lsp::Position::new(0, 25),
12715                                    lsp::Position::new(0, 28),
12716                                ),
12717                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12718                                ..Default::default()
12719                            },
12720                        ],
12721                    },
12722                    &[],
12723                    cx,
12724                )
12725                .unwrap()
12726        });
12727    });
12728
12729    executor.run_until_parked();
12730
12731    cx.update_editor(|editor, window, cx| {
12732        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12733    });
12734
12735    cx.assert_editor_state(indoc! {"
12736        fn func(abc def: i32) -> ˇu32 {
12737        }
12738    "});
12739
12740    cx.update_editor(|editor, window, cx| {
12741        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12742    });
12743
12744    cx.assert_editor_state(indoc! {"
12745        fn func(abc ˇdef: i32) -> u32 {
12746        }
12747    "});
12748
12749    cx.update_editor(|editor, window, cx| {
12750        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12751    });
12752
12753    cx.assert_editor_state(indoc! {"
12754        fn func(abcˇ def: i32) -> u32 {
12755        }
12756    "});
12757
12758    cx.update_editor(|editor, window, cx| {
12759        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12760    });
12761
12762    cx.assert_editor_state(indoc! {"
12763        fn func(abc def: i32) -> ˇu32 {
12764        }
12765    "});
12766}
12767
12768#[gpui::test]
12769async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12770    init_test(cx, |_| {});
12771
12772    let mut cx = EditorTestContext::new(cx).await;
12773
12774    cx.set_state(indoc! {"
12775        fn func(abˇc def: i32) -> u32 {
12776        }
12777    "});
12778    let lsp_store =
12779        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12780
12781    cx.update(|_, cx| {
12782        lsp_store.update(cx, |lsp_store, cx| {
12783            lsp_store.update_diagnostics(
12784                LanguageServerId(0),
12785                lsp::PublishDiagnosticsParams {
12786                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12787                    version: None,
12788                    diagnostics: vec![lsp::Diagnostic {
12789                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12790                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12791                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12792                        ..Default::default()
12793                    }],
12794                },
12795                &[],
12796                cx,
12797            )
12798        })
12799    }).unwrap();
12800    cx.run_until_parked();
12801    cx.update_editor(|editor, window, cx| {
12802        hover_popover::hover(editor, &Default::default(), window, cx)
12803    });
12804    cx.run_until_parked();
12805    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12806}
12807
12808#[gpui::test]
12809async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12810    init_test(cx, |_| {});
12811
12812    let mut cx = EditorTestContext::new(cx).await;
12813
12814    let diff_base = r#"
12815        use some::mod;
12816
12817        const A: u32 = 42;
12818
12819        fn main() {
12820            println!("hello");
12821
12822            println!("world");
12823        }
12824        "#
12825    .unindent();
12826
12827    // Edits are modified, removed, modified, added
12828    cx.set_state(
12829        &r#"
12830        use some::modified;
12831
12832        ˇ
12833        fn main() {
12834            println!("hello there");
12835
12836            println!("around the");
12837            println!("world");
12838        }
12839        "#
12840        .unindent(),
12841    );
12842
12843    cx.set_head_text(&diff_base);
12844    executor.run_until_parked();
12845
12846    cx.update_editor(|editor, window, cx| {
12847        //Wrap around the bottom of the buffer
12848        for _ in 0..3 {
12849            editor.go_to_next_hunk(&GoToHunk, 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        //Wrap around the top of the buffer
12870        for _ in 0..2 {
12871            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12872        }
12873    });
12874
12875    cx.assert_editor_state(
12876        &r#"
12877        use some::modified;
12878
12879
12880        fn main() {
12881        ˇ    println!("hello there");
12882
12883            println!("around the");
12884            println!("world");
12885        }
12886        "#
12887        .unindent(),
12888    );
12889
12890    cx.update_editor(|editor, window, cx| {
12891        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12892    });
12893
12894    cx.assert_editor_state(
12895        &r#"
12896        use some::modified;
12897
12898        ˇ
12899        fn main() {
12900            println!("hello there");
12901
12902            println!("around the");
12903            println!("world");
12904        }
12905        "#
12906        .unindent(),
12907    );
12908
12909    cx.update_editor(|editor, window, cx| {
12910        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12911    });
12912
12913    cx.assert_editor_state(
12914        &r#"
12915        ˇuse some::modified;
12916
12917
12918        fn main() {
12919            println!("hello there");
12920
12921            println!("around the");
12922            println!("world");
12923        }
12924        "#
12925        .unindent(),
12926    );
12927
12928    cx.update_editor(|editor, window, cx| {
12929        for _ in 0..2 {
12930            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12931        }
12932    });
12933
12934    cx.assert_editor_state(
12935        &r#"
12936        use some::modified;
12937
12938
12939        fn main() {
12940        ˇ    println!("hello there");
12941
12942            println!("around the");
12943            println!("world");
12944        }
12945        "#
12946        .unindent(),
12947    );
12948
12949    cx.update_editor(|editor, window, cx| {
12950        editor.fold(&Fold, window, cx);
12951    });
12952
12953    cx.update_editor(|editor, window, cx| {
12954        editor.go_to_next_hunk(&GoToHunk, window, cx);
12955    });
12956
12957    cx.assert_editor_state(
12958        &r#"
12959        ˇuse some::modified;
12960
12961
12962        fn main() {
12963            println!("hello there");
12964
12965            println!("around the");
12966            println!("world");
12967        }
12968        "#
12969        .unindent(),
12970    );
12971}
12972
12973#[test]
12974fn test_split_words() {
12975    fn split(text: &str) -> Vec<&str> {
12976        split_words(text).collect()
12977    }
12978
12979    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12980    assert_eq!(split("hello_world"), &["hello_", "world"]);
12981    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12982    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12983    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12984    assert_eq!(split("helloworld"), &["helloworld"]);
12985
12986    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12987}
12988
12989#[gpui::test]
12990async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12991    init_test(cx, |_| {});
12992
12993    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12994    let mut assert = |before, after| {
12995        let _state_context = cx.set_state(before);
12996        cx.run_until_parked();
12997        cx.update_editor(|editor, window, cx| {
12998            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12999        });
13000        cx.run_until_parked();
13001        cx.assert_editor_state(after);
13002    };
13003
13004    // Outside bracket jumps to outside of matching bracket
13005    assert("console.logˇ(var);", "console.log(var)ˇ;");
13006    assert("console.log(var)ˇ;", "console.logˇ(var);");
13007
13008    // Inside bracket jumps to inside of matching bracket
13009    assert("console.log(ˇvar);", "console.log(varˇ);");
13010    assert("console.log(varˇ);", "console.log(ˇvar);");
13011
13012    // When outside a bracket and inside, favor jumping to the inside bracket
13013    assert(
13014        "console.log('foo', [1, 2, 3]ˇ);",
13015        "console.log(ˇ'foo', [1, 2, 3]);",
13016    );
13017    assert(
13018        "console.log(ˇ'foo', [1, 2, 3]);",
13019        "console.log('foo', [1, 2, 3]ˇ);",
13020    );
13021
13022    // Bias forward if two options are equally likely
13023    assert(
13024        "let result = curried_fun()ˇ();",
13025        "let result = curried_fun()()ˇ;",
13026    );
13027
13028    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13029    assert(
13030        indoc! {"
13031            function test() {
13032                console.log('test')ˇ
13033            }"},
13034        indoc! {"
13035            function test() {
13036                console.logˇ('test')
13037            }"},
13038    );
13039}
13040
13041#[gpui::test]
13042async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13043    init_test(cx, |_| {});
13044
13045    let fs = FakeFs::new(cx.executor());
13046    fs.insert_tree(
13047        path!("/a"),
13048        json!({
13049            "main.rs": "fn main() { let a = 5; }",
13050            "other.rs": "// Test file",
13051        }),
13052    )
13053    .await;
13054    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13055
13056    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13057    language_registry.add(Arc::new(Language::new(
13058        LanguageConfig {
13059            name: "Rust".into(),
13060            matcher: LanguageMatcher {
13061                path_suffixes: vec!["rs".to_string()],
13062                ..Default::default()
13063            },
13064            brackets: BracketPairConfig {
13065                pairs: vec![BracketPair {
13066                    start: "{".to_string(),
13067                    end: "}".to_string(),
13068                    close: true,
13069                    surround: true,
13070                    newline: true,
13071                }],
13072                disabled_scopes_by_bracket_ix: Vec::new(),
13073            },
13074            ..Default::default()
13075        },
13076        Some(tree_sitter_rust::LANGUAGE.into()),
13077    )));
13078    let mut fake_servers = language_registry.register_fake_lsp(
13079        "Rust",
13080        FakeLspAdapter {
13081            capabilities: lsp::ServerCapabilities {
13082                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13083                    first_trigger_character: "{".to_string(),
13084                    more_trigger_character: None,
13085                }),
13086                ..Default::default()
13087            },
13088            ..Default::default()
13089        },
13090    );
13091
13092    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13093
13094    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13095
13096    let worktree_id = workspace
13097        .update(cx, |workspace, _, cx| {
13098            workspace.project().update(cx, |project, cx| {
13099                project.worktrees(cx).next().unwrap().read(cx).id()
13100            })
13101        })
13102        .unwrap();
13103
13104    let buffer = project
13105        .update(cx, |project, cx| {
13106            project.open_local_buffer(path!("/a/main.rs"), cx)
13107        })
13108        .await
13109        .unwrap();
13110    let editor_handle = workspace
13111        .update(cx, |workspace, window, cx| {
13112            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13113        })
13114        .unwrap()
13115        .await
13116        .unwrap()
13117        .downcast::<Editor>()
13118        .unwrap();
13119
13120    cx.executor().start_waiting();
13121    let fake_server = fake_servers.next().await.unwrap();
13122
13123    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13124        |params, _| async move {
13125            assert_eq!(
13126                params.text_document_position.text_document.uri,
13127                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13128            );
13129            assert_eq!(
13130                params.text_document_position.position,
13131                lsp::Position::new(0, 21),
13132            );
13133
13134            Ok(Some(vec![lsp::TextEdit {
13135                new_text: "]".to_string(),
13136                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13137            }]))
13138        },
13139    );
13140
13141    editor_handle.update_in(cx, |editor, window, cx| {
13142        window.focus(&editor.focus_handle(cx));
13143        editor.change_selections(None, window, cx, |s| {
13144            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13145        });
13146        editor.handle_input("{", window, cx);
13147    });
13148
13149    cx.executor().run_until_parked();
13150
13151    buffer.update(cx, |buffer, _| {
13152        assert_eq!(
13153            buffer.text(),
13154            "fn main() { let a = {5}; }",
13155            "No extra braces from on type formatting should appear in the buffer"
13156        )
13157    });
13158}
13159
13160#[gpui::test]
13161async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13162    init_test(cx, |_| {});
13163
13164    let fs = FakeFs::new(cx.executor());
13165    fs.insert_tree(
13166        path!("/a"),
13167        json!({
13168            "main.rs": "fn main() { let a = 5; }",
13169            "other.rs": "// Test file",
13170        }),
13171    )
13172    .await;
13173
13174    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13175
13176    let server_restarts = Arc::new(AtomicUsize::new(0));
13177    let closure_restarts = Arc::clone(&server_restarts);
13178    let language_server_name = "test language server";
13179    let language_name: LanguageName = "Rust".into();
13180
13181    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13182    language_registry.add(Arc::new(Language::new(
13183        LanguageConfig {
13184            name: language_name.clone(),
13185            matcher: LanguageMatcher {
13186                path_suffixes: vec!["rs".to_string()],
13187                ..Default::default()
13188            },
13189            ..Default::default()
13190        },
13191        Some(tree_sitter_rust::LANGUAGE.into()),
13192    )));
13193    let mut fake_servers = language_registry.register_fake_lsp(
13194        "Rust",
13195        FakeLspAdapter {
13196            name: language_server_name,
13197            initialization_options: Some(json!({
13198                "testOptionValue": true
13199            })),
13200            initializer: Some(Box::new(move |fake_server| {
13201                let task_restarts = Arc::clone(&closure_restarts);
13202                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13203                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13204                    futures::future::ready(Ok(()))
13205                });
13206            })),
13207            ..Default::default()
13208        },
13209    );
13210
13211    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13212    let _buffer = project
13213        .update(cx, |project, cx| {
13214            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13215        })
13216        .await
13217        .unwrap();
13218    let _fake_server = fake_servers.next().await.unwrap();
13219    update_test_language_settings(cx, |language_settings| {
13220        language_settings.languages.insert(
13221            language_name.clone(),
13222            LanguageSettingsContent {
13223                tab_size: NonZeroU32::new(8),
13224                ..Default::default()
13225            },
13226        );
13227    });
13228    cx.executor().run_until_parked();
13229    assert_eq!(
13230        server_restarts.load(atomic::Ordering::Acquire),
13231        0,
13232        "Should not restart LSP server on an unrelated change"
13233    );
13234
13235    update_test_project_settings(cx, |project_settings| {
13236        project_settings.lsp.insert(
13237            "Some other server name".into(),
13238            LspSettings {
13239                binary: None,
13240                settings: None,
13241                initialization_options: Some(json!({
13242                    "some other init value": false
13243                })),
13244                enable_lsp_tasks: false,
13245            },
13246        );
13247    });
13248    cx.executor().run_until_parked();
13249    assert_eq!(
13250        server_restarts.load(atomic::Ordering::Acquire),
13251        0,
13252        "Should not restart LSP server on an unrelated LSP settings change"
13253    );
13254
13255    update_test_project_settings(cx, |project_settings| {
13256        project_settings.lsp.insert(
13257            language_server_name.into(),
13258            LspSettings {
13259                binary: None,
13260                settings: None,
13261                initialization_options: Some(json!({
13262                    "anotherInitValue": false
13263                })),
13264                enable_lsp_tasks: false,
13265            },
13266        );
13267    });
13268    cx.executor().run_until_parked();
13269    assert_eq!(
13270        server_restarts.load(atomic::Ordering::Acquire),
13271        1,
13272        "Should restart LSP server on a related LSP settings change"
13273    );
13274
13275    update_test_project_settings(cx, |project_settings| {
13276        project_settings.lsp.insert(
13277            language_server_name.into(),
13278            LspSettings {
13279                binary: None,
13280                settings: None,
13281                initialization_options: Some(json!({
13282                    "anotherInitValue": false
13283                })),
13284                enable_lsp_tasks: false,
13285            },
13286        );
13287    });
13288    cx.executor().run_until_parked();
13289    assert_eq!(
13290        server_restarts.load(atomic::Ordering::Acquire),
13291        1,
13292        "Should not restart LSP server on a related LSP settings change that is the same"
13293    );
13294
13295    update_test_project_settings(cx, |project_settings| {
13296        project_settings.lsp.insert(
13297            language_server_name.into(),
13298            LspSettings {
13299                binary: None,
13300                settings: None,
13301                initialization_options: None,
13302                enable_lsp_tasks: false,
13303            },
13304        );
13305    });
13306    cx.executor().run_until_parked();
13307    assert_eq!(
13308        server_restarts.load(atomic::Ordering::Acquire),
13309        2,
13310        "Should restart LSP server on another related LSP settings change"
13311    );
13312}
13313
13314#[gpui::test]
13315async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13316    init_test(cx, |_| {});
13317
13318    let mut cx = EditorLspTestContext::new_rust(
13319        lsp::ServerCapabilities {
13320            completion_provider: Some(lsp::CompletionOptions {
13321                trigger_characters: Some(vec![".".to_string()]),
13322                resolve_provider: Some(true),
13323                ..Default::default()
13324            }),
13325            ..Default::default()
13326        },
13327        cx,
13328    )
13329    .await;
13330
13331    cx.set_state("fn main() { let a = 2ˇ; }");
13332    cx.simulate_keystroke(".");
13333    let completion_item = lsp::CompletionItem {
13334        label: "some".into(),
13335        kind: Some(lsp::CompletionItemKind::SNIPPET),
13336        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13337        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13338            kind: lsp::MarkupKind::Markdown,
13339            value: "```rust\nSome(2)\n```".to_string(),
13340        })),
13341        deprecated: Some(false),
13342        sort_text: Some("fffffff2".to_string()),
13343        filter_text: Some("some".to_string()),
13344        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13345        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13346            range: lsp::Range {
13347                start: lsp::Position {
13348                    line: 0,
13349                    character: 22,
13350                },
13351                end: lsp::Position {
13352                    line: 0,
13353                    character: 22,
13354                },
13355            },
13356            new_text: "Some(2)".to_string(),
13357        })),
13358        additional_text_edits: Some(vec![lsp::TextEdit {
13359            range: lsp::Range {
13360                start: lsp::Position {
13361                    line: 0,
13362                    character: 20,
13363                },
13364                end: lsp::Position {
13365                    line: 0,
13366                    character: 22,
13367                },
13368            },
13369            new_text: "".to_string(),
13370        }]),
13371        ..Default::default()
13372    };
13373
13374    let closure_completion_item = completion_item.clone();
13375    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13376        let task_completion_item = closure_completion_item.clone();
13377        async move {
13378            Ok(Some(lsp::CompletionResponse::Array(vec![
13379                task_completion_item,
13380            ])))
13381        }
13382    });
13383
13384    request.next().await;
13385
13386    cx.condition(|editor, _| editor.context_menu_visible())
13387        .await;
13388    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13389        editor
13390            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13391            .unwrap()
13392    });
13393    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13394
13395    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13396        let task_completion_item = completion_item.clone();
13397        async move { Ok(task_completion_item) }
13398    })
13399    .next()
13400    .await
13401    .unwrap();
13402    apply_additional_edits.await.unwrap();
13403    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13404}
13405
13406#[gpui::test]
13407async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13408    init_test(cx, |_| {});
13409
13410    let mut cx = EditorLspTestContext::new_rust(
13411        lsp::ServerCapabilities {
13412            completion_provider: Some(lsp::CompletionOptions {
13413                trigger_characters: Some(vec![".".to_string()]),
13414                resolve_provider: Some(true),
13415                ..Default::default()
13416            }),
13417            ..Default::default()
13418        },
13419        cx,
13420    )
13421    .await;
13422
13423    cx.set_state("fn main() { let a = 2ˇ; }");
13424    cx.simulate_keystroke(".");
13425
13426    let item1 = lsp::CompletionItem {
13427        label: "method id()".to_string(),
13428        filter_text: Some("id".to_string()),
13429        detail: None,
13430        documentation: None,
13431        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13432            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13433            new_text: ".id".to_string(),
13434        })),
13435        ..lsp::CompletionItem::default()
13436    };
13437
13438    let item2 = lsp::CompletionItem {
13439        label: "other".to_string(),
13440        filter_text: Some("other".to_string()),
13441        detail: None,
13442        documentation: None,
13443        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13444            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13445            new_text: ".other".to_string(),
13446        })),
13447        ..lsp::CompletionItem::default()
13448    };
13449
13450    let item1 = item1.clone();
13451    cx.set_request_handler::<lsp::request::Completion, _, _>({
13452        let item1 = item1.clone();
13453        move |_, _, _| {
13454            let item1 = item1.clone();
13455            let item2 = item2.clone();
13456            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13457        }
13458    })
13459    .next()
13460    .await;
13461
13462    cx.condition(|editor, _| editor.context_menu_visible())
13463        .await;
13464    cx.update_editor(|editor, _, _| {
13465        let context_menu = editor.context_menu.borrow_mut();
13466        let context_menu = context_menu
13467            .as_ref()
13468            .expect("Should have the context menu deployed");
13469        match context_menu {
13470            CodeContextMenu::Completions(completions_menu) => {
13471                let completions = completions_menu.completions.borrow_mut();
13472                assert_eq!(
13473                    completions
13474                        .iter()
13475                        .map(|completion| &completion.label.text)
13476                        .collect::<Vec<_>>(),
13477                    vec!["method id()", "other"]
13478                )
13479            }
13480            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13481        }
13482    });
13483
13484    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13485        let item1 = item1.clone();
13486        move |_, item_to_resolve, _| {
13487            let item1 = item1.clone();
13488            async move {
13489                if item1 == item_to_resolve {
13490                    Ok(lsp::CompletionItem {
13491                        label: "method id()".to_string(),
13492                        filter_text: Some("id".to_string()),
13493                        detail: Some("Now resolved!".to_string()),
13494                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13495                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13496                            range: lsp::Range::new(
13497                                lsp::Position::new(0, 22),
13498                                lsp::Position::new(0, 22),
13499                            ),
13500                            new_text: ".id".to_string(),
13501                        })),
13502                        ..lsp::CompletionItem::default()
13503                    })
13504                } else {
13505                    Ok(item_to_resolve)
13506                }
13507            }
13508        }
13509    })
13510    .next()
13511    .await
13512    .unwrap();
13513    cx.run_until_parked();
13514
13515    cx.update_editor(|editor, window, cx| {
13516        editor.context_menu_next(&Default::default(), window, cx);
13517    });
13518
13519    cx.update_editor(|editor, _, _| {
13520        let context_menu = editor.context_menu.borrow_mut();
13521        let context_menu = context_menu
13522            .as_ref()
13523            .expect("Should have the context menu deployed");
13524        match context_menu {
13525            CodeContextMenu::Completions(completions_menu) => {
13526                let completions = completions_menu.completions.borrow_mut();
13527                assert_eq!(
13528                    completions
13529                        .iter()
13530                        .map(|completion| &completion.label.text)
13531                        .collect::<Vec<_>>(),
13532                    vec!["method id() Now resolved!", "other"],
13533                    "Should update first completion label, but not second as the filter text did not match."
13534                );
13535            }
13536            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13537        }
13538    });
13539}
13540
13541#[gpui::test]
13542async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13543    init_test(cx, |_| {});
13544
13545    let mut cx = EditorLspTestContext::new_rust(
13546        lsp::ServerCapabilities {
13547            completion_provider: Some(lsp::CompletionOptions {
13548                trigger_characters: Some(vec![".".to_string()]),
13549                resolve_provider: Some(true),
13550                ..Default::default()
13551            }),
13552            ..Default::default()
13553        },
13554        cx,
13555    )
13556    .await;
13557
13558    cx.set_state("fn main() { let a = 2ˇ; }");
13559    cx.simulate_keystroke(".");
13560
13561    let unresolved_item_1 = lsp::CompletionItem {
13562        label: "id".to_string(),
13563        filter_text: Some("id".to_string()),
13564        detail: None,
13565        documentation: None,
13566        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13567            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13568            new_text: ".id".to_string(),
13569        })),
13570        ..lsp::CompletionItem::default()
13571    };
13572    let resolved_item_1 = lsp::CompletionItem {
13573        additional_text_edits: Some(vec![lsp::TextEdit {
13574            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13575            new_text: "!!".to_string(),
13576        }]),
13577        ..unresolved_item_1.clone()
13578    };
13579    let unresolved_item_2 = lsp::CompletionItem {
13580        label: "other".to_string(),
13581        filter_text: Some("other".to_string()),
13582        detail: None,
13583        documentation: None,
13584        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13585            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13586            new_text: ".other".to_string(),
13587        })),
13588        ..lsp::CompletionItem::default()
13589    };
13590    let resolved_item_2 = lsp::CompletionItem {
13591        additional_text_edits: Some(vec![lsp::TextEdit {
13592            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13593            new_text: "??".to_string(),
13594        }]),
13595        ..unresolved_item_2.clone()
13596    };
13597
13598    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13599    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13600    cx.lsp
13601        .server
13602        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13603            let unresolved_item_1 = unresolved_item_1.clone();
13604            let resolved_item_1 = resolved_item_1.clone();
13605            let unresolved_item_2 = unresolved_item_2.clone();
13606            let resolved_item_2 = resolved_item_2.clone();
13607            let resolve_requests_1 = resolve_requests_1.clone();
13608            let resolve_requests_2 = resolve_requests_2.clone();
13609            move |unresolved_request, _| {
13610                let unresolved_item_1 = unresolved_item_1.clone();
13611                let resolved_item_1 = resolved_item_1.clone();
13612                let unresolved_item_2 = unresolved_item_2.clone();
13613                let resolved_item_2 = resolved_item_2.clone();
13614                let resolve_requests_1 = resolve_requests_1.clone();
13615                let resolve_requests_2 = resolve_requests_2.clone();
13616                async move {
13617                    if unresolved_request == unresolved_item_1 {
13618                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13619                        Ok(resolved_item_1.clone())
13620                    } else if unresolved_request == unresolved_item_2 {
13621                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13622                        Ok(resolved_item_2.clone())
13623                    } else {
13624                        panic!("Unexpected completion item {unresolved_request:?}")
13625                    }
13626                }
13627            }
13628        })
13629        .detach();
13630
13631    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13632        let unresolved_item_1 = unresolved_item_1.clone();
13633        let unresolved_item_2 = unresolved_item_2.clone();
13634        async move {
13635            Ok(Some(lsp::CompletionResponse::Array(vec![
13636                unresolved_item_1,
13637                unresolved_item_2,
13638            ])))
13639        }
13640    })
13641    .next()
13642    .await;
13643
13644    cx.condition(|editor, _| editor.context_menu_visible())
13645        .await;
13646    cx.update_editor(|editor, _, _| {
13647        let context_menu = editor.context_menu.borrow_mut();
13648        let context_menu = context_menu
13649            .as_ref()
13650            .expect("Should have the context menu deployed");
13651        match context_menu {
13652            CodeContextMenu::Completions(completions_menu) => {
13653                let completions = completions_menu.completions.borrow_mut();
13654                assert_eq!(
13655                    completions
13656                        .iter()
13657                        .map(|completion| &completion.label.text)
13658                        .collect::<Vec<_>>(),
13659                    vec!["id", "other"]
13660                )
13661            }
13662            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13663        }
13664    });
13665    cx.run_until_parked();
13666
13667    cx.update_editor(|editor, window, cx| {
13668        editor.context_menu_next(&ContextMenuNext, window, cx);
13669    });
13670    cx.run_until_parked();
13671    cx.update_editor(|editor, window, cx| {
13672        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13673    });
13674    cx.run_until_parked();
13675    cx.update_editor(|editor, window, cx| {
13676        editor.context_menu_next(&ContextMenuNext, window, cx);
13677    });
13678    cx.run_until_parked();
13679    cx.update_editor(|editor, window, cx| {
13680        editor
13681            .compose_completion(&ComposeCompletion::default(), window, cx)
13682            .expect("No task returned")
13683    })
13684    .await
13685    .expect("Completion failed");
13686    cx.run_until_parked();
13687
13688    cx.update_editor(|editor, _, cx| {
13689        assert_eq!(
13690            resolve_requests_1.load(atomic::Ordering::Acquire),
13691            1,
13692            "Should always resolve once despite multiple selections"
13693        );
13694        assert_eq!(
13695            resolve_requests_2.load(atomic::Ordering::Acquire),
13696            1,
13697            "Should always resolve once after multiple selections and applying the completion"
13698        );
13699        assert_eq!(
13700            editor.text(cx),
13701            "fn main() { let a = ??.other; }",
13702            "Should use resolved data when applying the completion"
13703        );
13704    });
13705}
13706
13707#[gpui::test]
13708async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13709    init_test(cx, |_| {});
13710
13711    let item_0 = lsp::CompletionItem {
13712        label: "abs".into(),
13713        insert_text: Some("abs".into()),
13714        data: Some(json!({ "very": "special"})),
13715        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13716        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13717            lsp::InsertReplaceEdit {
13718                new_text: "abs".to_string(),
13719                insert: lsp::Range::default(),
13720                replace: lsp::Range::default(),
13721            },
13722        )),
13723        ..lsp::CompletionItem::default()
13724    };
13725    let items = iter::once(item_0.clone())
13726        .chain((11..51).map(|i| lsp::CompletionItem {
13727            label: format!("item_{}", i),
13728            insert_text: Some(format!("item_{}", i)),
13729            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13730            ..lsp::CompletionItem::default()
13731        }))
13732        .collect::<Vec<_>>();
13733
13734    let default_commit_characters = vec!["?".to_string()];
13735    let default_data = json!({ "default": "data"});
13736    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13737    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13738    let default_edit_range = lsp::Range {
13739        start: lsp::Position {
13740            line: 0,
13741            character: 5,
13742        },
13743        end: lsp::Position {
13744            line: 0,
13745            character: 5,
13746        },
13747    };
13748
13749    let mut cx = EditorLspTestContext::new_rust(
13750        lsp::ServerCapabilities {
13751            completion_provider: Some(lsp::CompletionOptions {
13752                trigger_characters: Some(vec![".".to_string()]),
13753                resolve_provider: Some(true),
13754                ..Default::default()
13755            }),
13756            ..Default::default()
13757        },
13758        cx,
13759    )
13760    .await;
13761
13762    cx.set_state("fn main() { let a = 2ˇ; }");
13763    cx.simulate_keystroke(".");
13764
13765    let completion_data = default_data.clone();
13766    let completion_characters = default_commit_characters.clone();
13767    let completion_items = items.clone();
13768    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13769        let default_data = completion_data.clone();
13770        let default_commit_characters = completion_characters.clone();
13771        let items = completion_items.clone();
13772        async move {
13773            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13774                items,
13775                item_defaults: Some(lsp::CompletionListItemDefaults {
13776                    data: Some(default_data.clone()),
13777                    commit_characters: Some(default_commit_characters.clone()),
13778                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13779                        default_edit_range,
13780                    )),
13781                    insert_text_format: Some(default_insert_text_format),
13782                    insert_text_mode: Some(default_insert_text_mode),
13783                }),
13784                ..lsp::CompletionList::default()
13785            })))
13786        }
13787    })
13788    .next()
13789    .await;
13790
13791    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13792    cx.lsp
13793        .server
13794        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13795            let closure_resolved_items = resolved_items.clone();
13796            move |item_to_resolve, _| {
13797                let closure_resolved_items = closure_resolved_items.clone();
13798                async move {
13799                    closure_resolved_items.lock().push(item_to_resolve.clone());
13800                    Ok(item_to_resolve)
13801                }
13802            }
13803        })
13804        .detach();
13805
13806    cx.condition(|editor, _| editor.context_menu_visible())
13807        .await;
13808    cx.run_until_parked();
13809    cx.update_editor(|editor, _, _| {
13810        let menu = editor.context_menu.borrow_mut();
13811        match menu.as_ref().expect("should have the completions menu") {
13812            CodeContextMenu::Completions(completions_menu) => {
13813                assert_eq!(
13814                    completions_menu
13815                        .entries
13816                        .borrow()
13817                        .iter()
13818                        .map(|mat| mat.string.clone())
13819                        .collect::<Vec<String>>(),
13820                    items
13821                        .iter()
13822                        .map(|completion| completion.label.clone())
13823                        .collect::<Vec<String>>()
13824                );
13825            }
13826            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13827        }
13828    });
13829    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13830    // with 4 from the end.
13831    assert_eq!(
13832        *resolved_items.lock(),
13833        [&items[0..16], &items[items.len() - 4..items.len()]]
13834            .concat()
13835            .iter()
13836            .cloned()
13837            .map(|mut item| {
13838                if item.data.is_none() {
13839                    item.data = Some(default_data.clone());
13840                }
13841                item
13842            })
13843            .collect::<Vec<lsp::CompletionItem>>(),
13844        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13845    );
13846    resolved_items.lock().clear();
13847
13848    cx.update_editor(|editor, window, cx| {
13849        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13850    });
13851    cx.run_until_parked();
13852    // Completions that have already been resolved are skipped.
13853    assert_eq!(
13854        *resolved_items.lock(),
13855        items[items.len() - 16..items.len() - 4]
13856            .iter()
13857            .cloned()
13858            .map(|mut item| {
13859                if item.data.is_none() {
13860                    item.data = Some(default_data.clone());
13861                }
13862                item
13863            })
13864            .collect::<Vec<lsp::CompletionItem>>()
13865    );
13866    resolved_items.lock().clear();
13867}
13868
13869#[gpui::test]
13870async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13871    init_test(cx, |_| {});
13872
13873    let mut cx = EditorLspTestContext::new(
13874        Language::new(
13875            LanguageConfig {
13876                matcher: LanguageMatcher {
13877                    path_suffixes: vec!["jsx".into()],
13878                    ..Default::default()
13879                },
13880                overrides: [(
13881                    "element".into(),
13882                    LanguageConfigOverride {
13883                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13884                        ..Default::default()
13885                    },
13886                )]
13887                .into_iter()
13888                .collect(),
13889                ..Default::default()
13890            },
13891            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13892        )
13893        .with_override_query("(jsx_self_closing_element) @element")
13894        .unwrap(),
13895        lsp::ServerCapabilities {
13896            completion_provider: Some(lsp::CompletionOptions {
13897                trigger_characters: Some(vec![":".to_string()]),
13898                ..Default::default()
13899            }),
13900            ..Default::default()
13901        },
13902        cx,
13903    )
13904    .await;
13905
13906    cx.lsp
13907        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13908            Ok(Some(lsp::CompletionResponse::Array(vec![
13909                lsp::CompletionItem {
13910                    label: "bg-blue".into(),
13911                    ..Default::default()
13912                },
13913                lsp::CompletionItem {
13914                    label: "bg-red".into(),
13915                    ..Default::default()
13916                },
13917                lsp::CompletionItem {
13918                    label: "bg-yellow".into(),
13919                    ..Default::default()
13920                },
13921            ])))
13922        });
13923
13924    cx.set_state(r#"<p class="bgˇ" />"#);
13925
13926    // Trigger completion when typing a dash, because the dash is an extra
13927    // word character in the 'element' scope, which contains the cursor.
13928    cx.simulate_keystroke("-");
13929    cx.executor().run_until_parked();
13930    cx.update_editor(|editor, _, _| {
13931        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13932        {
13933            assert_eq!(
13934                completion_menu_entries(&menu),
13935                &["bg-red", "bg-blue", "bg-yellow"]
13936            );
13937        } else {
13938            panic!("expected completion menu to be open");
13939        }
13940    });
13941
13942    cx.simulate_keystroke("l");
13943    cx.executor().run_until_parked();
13944    cx.update_editor(|editor, _, _| {
13945        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13946        {
13947            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13948        } else {
13949            panic!("expected completion menu to be open");
13950        }
13951    });
13952
13953    // When filtering completions, consider the character after the '-' to
13954    // be the start of a subword.
13955    cx.set_state(r#"<p class="yelˇ" />"#);
13956    cx.simulate_keystroke("l");
13957    cx.executor().run_until_parked();
13958    cx.update_editor(|editor, _, _| {
13959        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13960        {
13961            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13962        } else {
13963            panic!("expected completion menu to be open");
13964        }
13965    });
13966}
13967
13968fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13969    let entries = menu.entries.borrow();
13970    entries.iter().map(|mat| mat.string.clone()).collect()
13971}
13972
13973#[gpui::test]
13974async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13975    init_test(cx, |settings| {
13976        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13977            FormatterList(vec![Formatter::Prettier].into()),
13978        ))
13979    });
13980
13981    let fs = FakeFs::new(cx.executor());
13982    fs.insert_file(path!("/file.ts"), Default::default()).await;
13983
13984    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13985    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13986
13987    language_registry.add(Arc::new(Language::new(
13988        LanguageConfig {
13989            name: "TypeScript".into(),
13990            matcher: LanguageMatcher {
13991                path_suffixes: vec!["ts".to_string()],
13992                ..Default::default()
13993            },
13994            ..Default::default()
13995        },
13996        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13997    )));
13998    update_test_language_settings(cx, |settings| {
13999        settings.defaults.prettier = Some(PrettierSettings {
14000            allowed: true,
14001            ..PrettierSettings::default()
14002        });
14003    });
14004
14005    let test_plugin = "test_plugin";
14006    let _ = language_registry.register_fake_lsp(
14007        "TypeScript",
14008        FakeLspAdapter {
14009            prettier_plugins: vec![test_plugin],
14010            ..Default::default()
14011        },
14012    );
14013
14014    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14015    let buffer = project
14016        .update(cx, |project, cx| {
14017            project.open_local_buffer(path!("/file.ts"), cx)
14018        })
14019        .await
14020        .unwrap();
14021
14022    let buffer_text = "one\ntwo\nthree\n";
14023    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14024    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14025    editor.update_in(cx, |editor, window, cx| {
14026        editor.set_text(buffer_text, window, cx)
14027    });
14028
14029    editor
14030        .update_in(cx, |editor, window, cx| {
14031            editor.perform_format(
14032                project.clone(),
14033                FormatTrigger::Manual,
14034                FormatTarget::Buffers,
14035                window,
14036                cx,
14037            )
14038        })
14039        .unwrap()
14040        .await;
14041    assert_eq!(
14042        editor.update(cx, |editor, cx| editor.text(cx)),
14043        buffer_text.to_string() + prettier_format_suffix,
14044        "Test prettier formatting was not applied to the original buffer text",
14045    );
14046
14047    update_test_language_settings(cx, |settings| {
14048        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14049    });
14050    let format = editor.update_in(cx, |editor, window, cx| {
14051        editor.perform_format(
14052            project.clone(),
14053            FormatTrigger::Manual,
14054            FormatTarget::Buffers,
14055            window,
14056            cx,
14057        )
14058    });
14059    format.await.unwrap();
14060    assert_eq!(
14061        editor.update(cx, |editor, cx| editor.text(cx)),
14062        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14063        "Autoformatting (via test prettier) was not applied to the original buffer text",
14064    );
14065}
14066
14067#[gpui::test]
14068async fn test_addition_reverts(cx: &mut TestAppContext) {
14069    init_test(cx, |_| {});
14070    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14071    let base_text = indoc! {r#"
14072        struct Row;
14073        struct Row1;
14074        struct Row2;
14075
14076        struct Row4;
14077        struct Row5;
14078        struct Row6;
14079
14080        struct Row8;
14081        struct Row9;
14082        struct Row10;"#};
14083
14084    // When addition hunks are not adjacent to carets, no hunk revert is performed
14085    assert_hunk_revert(
14086        indoc! {r#"struct Row;
14087                   struct Row1;
14088                   struct Row1.1;
14089                   struct Row1.2;
14090                   struct Row2;ˇ
14091
14092                   struct Row4;
14093                   struct Row5;
14094                   struct Row6;
14095
14096                   struct Row8;
14097                   ˇstruct Row9;
14098                   struct Row9.1;
14099                   struct Row9.2;
14100                   struct Row9.3;
14101                   struct Row10;"#},
14102        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14103        indoc! {r#"struct Row;
14104                   struct Row1;
14105                   struct Row1.1;
14106                   struct Row1.2;
14107                   struct Row2;ˇ
14108
14109                   struct Row4;
14110                   struct Row5;
14111                   struct Row6;
14112
14113                   struct Row8;
14114                   ˇstruct Row9;
14115                   struct Row9.1;
14116                   struct Row9.2;
14117                   struct Row9.3;
14118                   struct Row10;"#},
14119        base_text,
14120        &mut cx,
14121    );
14122    // Same for selections
14123    assert_hunk_revert(
14124        indoc! {r#"struct Row;
14125                   struct Row1;
14126                   struct Row2;
14127                   struct Row2.1;
14128                   struct Row2.2;
14129                   «ˇ
14130                   struct Row4;
14131                   struct» Row5;
14132                   «struct Row6;
14133                   ˇ»
14134                   struct Row9.1;
14135                   struct Row9.2;
14136                   struct Row9.3;
14137                   struct Row8;
14138                   struct Row9;
14139                   struct Row10;"#},
14140        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14141        indoc! {r#"struct Row;
14142                   struct Row1;
14143                   struct Row2;
14144                   struct Row2.1;
14145                   struct Row2.2;
14146                   «ˇ
14147                   struct Row4;
14148                   struct» Row5;
14149                   «struct Row6;
14150                   ˇ»
14151                   struct Row9.1;
14152                   struct Row9.2;
14153                   struct Row9.3;
14154                   struct Row8;
14155                   struct Row9;
14156                   struct Row10;"#},
14157        base_text,
14158        &mut cx,
14159    );
14160
14161    // When carets and selections intersect the addition hunks, those are reverted.
14162    // Adjacent carets got merged.
14163    assert_hunk_revert(
14164        indoc! {r#"struct Row;
14165                   ˇ// something on the top
14166                   struct Row1;
14167                   struct Row2;
14168                   struct Roˇw3.1;
14169                   struct Row2.2;
14170                   struct Row2.3;ˇ
14171
14172                   struct Row4;
14173                   struct ˇRow5.1;
14174                   struct Row5.2;
14175                   struct «Rowˇ»5.3;
14176                   struct Row5;
14177                   struct Row6;
14178                   ˇ
14179                   struct Row9.1;
14180                   struct «Rowˇ»9.2;
14181                   struct «ˇRow»9.3;
14182                   struct Row8;
14183                   struct Row9;
14184                   «ˇ// something on bottom»
14185                   struct Row10;"#},
14186        vec![
14187            DiffHunkStatusKind::Added,
14188            DiffHunkStatusKind::Added,
14189            DiffHunkStatusKind::Added,
14190            DiffHunkStatusKind::Added,
14191            DiffHunkStatusKind::Added,
14192        ],
14193        indoc! {r#"struct Row;
14194                   ˇstruct Row1;
14195                   struct Row2;
14196                   ˇ
14197                   struct Row4;
14198                   ˇstruct Row5;
14199                   struct Row6;
14200                   ˇ
14201                   ˇstruct Row8;
14202                   struct Row9;
14203                   ˇstruct Row10;"#},
14204        base_text,
14205        &mut cx,
14206    );
14207}
14208
14209#[gpui::test]
14210async fn test_modification_reverts(cx: &mut TestAppContext) {
14211    init_test(cx, |_| {});
14212    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14213    let base_text = indoc! {r#"
14214        struct Row;
14215        struct Row1;
14216        struct Row2;
14217
14218        struct Row4;
14219        struct Row5;
14220        struct Row6;
14221
14222        struct Row8;
14223        struct Row9;
14224        struct Row10;"#};
14225
14226    // Modification hunks behave the same as the addition ones.
14227    assert_hunk_revert(
14228        indoc! {r#"struct Row;
14229                   struct Row1;
14230                   struct Row33;
14231                   ˇ
14232                   struct Row4;
14233                   struct Row5;
14234                   struct Row6;
14235                   ˇ
14236                   struct Row99;
14237                   struct Row9;
14238                   struct Row10;"#},
14239        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14240        indoc! {r#"struct Row;
14241                   struct Row1;
14242                   struct Row33;
14243                   ˇ
14244                   struct Row4;
14245                   struct Row5;
14246                   struct Row6;
14247                   ˇ
14248                   struct Row99;
14249                   struct Row9;
14250                   struct Row10;"#},
14251        base_text,
14252        &mut cx,
14253    );
14254    assert_hunk_revert(
14255        indoc! {r#"struct Row;
14256                   struct Row1;
14257                   struct Row33;
14258                   «ˇ
14259                   struct Row4;
14260                   struct» Row5;
14261                   «struct Row6;
14262                   ˇ»
14263                   struct Row99;
14264                   struct Row9;
14265                   struct Row10;"#},
14266        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14267        indoc! {r#"struct Row;
14268                   struct Row1;
14269                   struct Row33;
14270                   «ˇ
14271                   struct Row4;
14272                   struct» Row5;
14273                   «struct Row6;
14274                   ˇ»
14275                   struct Row99;
14276                   struct Row9;
14277                   struct Row10;"#},
14278        base_text,
14279        &mut cx,
14280    );
14281
14282    assert_hunk_revert(
14283        indoc! {r#"ˇstruct Row1.1;
14284                   struct Row1;
14285                   «ˇstr»uct Row22;
14286
14287                   struct ˇRow44;
14288                   struct Row5;
14289                   struct «Rˇ»ow66;ˇ
14290
14291                   «struˇ»ct Row88;
14292                   struct Row9;
14293                   struct Row1011;ˇ"#},
14294        vec![
14295            DiffHunkStatusKind::Modified,
14296            DiffHunkStatusKind::Modified,
14297            DiffHunkStatusKind::Modified,
14298            DiffHunkStatusKind::Modified,
14299            DiffHunkStatusKind::Modified,
14300            DiffHunkStatusKind::Modified,
14301        ],
14302        indoc! {r#"struct Row;
14303                   ˇstruct Row1;
14304                   struct Row2;
14305                   ˇ
14306                   struct Row4;
14307                   ˇstruct Row5;
14308                   struct Row6;
14309                   ˇ
14310                   struct Row8;
14311                   ˇstruct Row9;
14312                   struct Row10;ˇ"#},
14313        base_text,
14314        &mut cx,
14315    );
14316}
14317
14318#[gpui::test]
14319async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14320    init_test(cx, |_| {});
14321    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14322    let base_text = indoc! {r#"
14323        one
14324
14325        two
14326        three
14327        "#};
14328
14329    cx.set_head_text(base_text);
14330    cx.set_state("\nˇ\n");
14331    cx.executor().run_until_parked();
14332    cx.update_editor(|editor, _window, cx| {
14333        editor.expand_selected_diff_hunks(cx);
14334    });
14335    cx.executor().run_until_parked();
14336    cx.update_editor(|editor, window, cx| {
14337        editor.backspace(&Default::default(), window, cx);
14338    });
14339    cx.run_until_parked();
14340    cx.assert_state_with_diff(
14341        indoc! {r#"
14342
14343        - two
14344        - threeˇ
14345        +
14346        "#}
14347        .to_string(),
14348    );
14349}
14350
14351#[gpui::test]
14352async fn test_deletion_reverts(cx: &mut TestAppContext) {
14353    init_test(cx, |_| {});
14354    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14355    let base_text = indoc! {r#"struct Row;
14356struct Row1;
14357struct Row2;
14358
14359struct Row4;
14360struct Row5;
14361struct Row6;
14362
14363struct Row8;
14364struct Row9;
14365struct Row10;"#};
14366
14367    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14368    assert_hunk_revert(
14369        indoc! {r#"struct Row;
14370                   struct Row2;
14371
14372                   ˇstruct Row4;
14373                   struct Row5;
14374                   struct Row6;
14375                   ˇ
14376                   struct Row8;
14377                   struct Row10;"#},
14378        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14379        indoc! {r#"struct Row;
14380                   struct Row2;
14381
14382                   ˇstruct Row4;
14383                   struct Row5;
14384                   struct Row6;
14385                   ˇ
14386                   struct Row8;
14387                   struct Row10;"#},
14388        base_text,
14389        &mut cx,
14390    );
14391    assert_hunk_revert(
14392        indoc! {r#"struct Row;
14393                   struct Row2;
14394
14395                   «ˇstruct Row4;
14396                   struct» Row5;
14397                   «struct Row6;
14398                   ˇ»
14399                   struct Row8;
14400                   struct Row10;"#},
14401        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14402        indoc! {r#"struct Row;
14403                   struct Row2;
14404
14405                   «ˇstruct Row4;
14406                   struct» Row5;
14407                   «struct Row6;
14408                   ˇ»
14409                   struct Row8;
14410                   struct Row10;"#},
14411        base_text,
14412        &mut cx,
14413    );
14414
14415    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14416    assert_hunk_revert(
14417        indoc! {r#"struct Row;
14418                   ˇstruct Row2;
14419
14420                   struct Row4;
14421                   struct Row5;
14422                   struct Row6;
14423
14424                   struct Row8;ˇ
14425                   struct Row10;"#},
14426        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14427        indoc! {r#"struct Row;
14428                   struct Row1;
14429                   ˇstruct Row2;
14430
14431                   struct Row4;
14432                   struct Row5;
14433                   struct Row6;
14434
14435                   struct Row8;ˇ
14436                   struct Row9;
14437                   struct Row10;"#},
14438        base_text,
14439        &mut cx,
14440    );
14441    assert_hunk_revert(
14442        indoc! {r#"struct Row;
14443                   struct Row2«ˇ;
14444                   struct Row4;
14445                   struct» Row5;
14446                   «struct Row6;
14447
14448                   struct Row8;ˇ»
14449                   struct Row10;"#},
14450        vec![
14451            DiffHunkStatusKind::Deleted,
14452            DiffHunkStatusKind::Deleted,
14453            DiffHunkStatusKind::Deleted,
14454        ],
14455        indoc! {r#"struct Row;
14456                   struct Row1;
14457                   struct Row2«ˇ;
14458
14459                   struct Row4;
14460                   struct» Row5;
14461                   «struct Row6;
14462
14463                   struct Row8;ˇ»
14464                   struct Row9;
14465                   struct Row10;"#},
14466        base_text,
14467        &mut cx,
14468    );
14469}
14470
14471#[gpui::test]
14472async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14473    init_test(cx, |_| {});
14474
14475    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14476    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14477    let base_text_3 =
14478        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14479
14480    let text_1 = edit_first_char_of_every_line(base_text_1);
14481    let text_2 = edit_first_char_of_every_line(base_text_2);
14482    let text_3 = edit_first_char_of_every_line(base_text_3);
14483
14484    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14485    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14486    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14487
14488    let multibuffer = cx.new(|cx| {
14489        let mut multibuffer = MultiBuffer::new(ReadWrite);
14490        multibuffer.push_excerpts(
14491            buffer_1.clone(),
14492            [
14493                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14494                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14495                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14496            ],
14497            cx,
14498        );
14499        multibuffer.push_excerpts(
14500            buffer_2.clone(),
14501            [
14502                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14503                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14504                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14505            ],
14506            cx,
14507        );
14508        multibuffer.push_excerpts(
14509            buffer_3.clone(),
14510            [
14511                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14512                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14513                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14514            ],
14515            cx,
14516        );
14517        multibuffer
14518    });
14519
14520    let fs = FakeFs::new(cx.executor());
14521    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14522    let (editor, cx) = cx
14523        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14524    editor.update_in(cx, |editor, _window, cx| {
14525        for (buffer, diff_base) in [
14526            (buffer_1.clone(), base_text_1),
14527            (buffer_2.clone(), base_text_2),
14528            (buffer_3.clone(), base_text_3),
14529        ] {
14530            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14531            editor
14532                .buffer
14533                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14534        }
14535    });
14536    cx.executor().run_until_parked();
14537
14538    editor.update_in(cx, |editor, window, cx| {
14539        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}");
14540        editor.select_all(&SelectAll, window, cx);
14541        editor.git_restore(&Default::default(), window, cx);
14542    });
14543    cx.executor().run_until_parked();
14544
14545    // When all ranges are selected, all buffer hunks are reverted.
14546    editor.update(cx, |editor, cx| {
14547        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");
14548    });
14549    buffer_1.update(cx, |buffer, _| {
14550        assert_eq!(buffer.text(), base_text_1);
14551    });
14552    buffer_2.update(cx, |buffer, _| {
14553        assert_eq!(buffer.text(), base_text_2);
14554    });
14555    buffer_3.update(cx, |buffer, _| {
14556        assert_eq!(buffer.text(), base_text_3);
14557    });
14558
14559    editor.update_in(cx, |editor, window, cx| {
14560        editor.undo(&Default::default(), window, cx);
14561    });
14562
14563    editor.update_in(cx, |editor, window, cx| {
14564        editor.change_selections(None, window, cx, |s| {
14565            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14566        });
14567        editor.git_restore(&Default::default(), window, cx);
14568    });
14569
14570    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14571    // but not affect buffer_2 and its related excerpts.
14572    editor.update(cx, |editor, cx| {
14573        assert_eq!(
14574            editor.text(cx),
14575            "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}"
14576        );
14577    });
14578    buffer_1.update(cx, |buffer, _| {
14579        assert_eq!(buffer.text(), base_text_1);
14580    });
14581    buffer_2.update(cx, |buffer, _| {
14582        assert_eq!(
14583            buffer.text(),
14584            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14585        );
14586    });
14587    buffer_3.update(cx, |buffer, _| {
14588        assert_eq!(
14589            buffer.text(),
14590            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14591        );
14592    });
14593
14594    fn edit_first_char_of_every_line(text: &str) -> String {
14595        text.split('\n')
14596            .map(|line| format!("X{}", &line[1..]))
14597            .collect::<Vec<_>>()
14598            .join("\n")
14599    }
14600}
14601
14602#[gpui::test]
14603async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14604    init_test(cx, |_| {});
14605
14606    let cols = 4;
14607    let rows = 10;
14608    let sample_text_1 = sample_text(rows, cols, 'a');
14609    assert_eq!(
14610        sample_text_1,
14611        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14612    );
14613    let sample_text_2 = sample_text(rows, cols, 'l');
14614    assert_eq!(
14615        sample_text_2,
14616        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14617    );
14618    let sample_text_3 = sample_text(rows, cols, 'v');
14619    assert_eq!(
14620        sample_text_3,
14621        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14622    );
14623
14624    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14625    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14626    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14627
14628    let multi_buffer = cx.new(|cx| {
14629        let mut multibuffer = MultiBuffer::new(ReadWrite);
14630        multibuffer.push_excerpts(
14631            buffer_1.clone(),
14632            [
14633                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14634                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14635                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14636            ],
14637            cx,
14638        );
14639        multibuffer.push_excerpts(
14640            buffer_2.clone(),
14641            [
14642                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14643                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14644                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14645            ],
14646            cx,
14647        );
14648        multibuffer.push_excerpts(
14649            buffer_3.clone(),
14650            [
14651                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14652                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14653                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14654            ],
14655            cx,
14656        );
14657        multibuffer
14658    });
14659
14660    let fs = FakeFs::new(cx.executor());
14661    fs.insert_tree(
14662        "/a",
14663        json!({
14664            "main.rs": sample_text_1,
14665            "other.rs": sample_text_2,
14666            "lib.rs": sample_text_3,
14667        }),
14668    )
14669    .await;
14670    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14671    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14672    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14673    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14674        Editor::new(
14675            EditorMode::full(),
14676            multi_buffer,
14677            Some(project.clone()),
14678            window,
14679            cx,
14680        )
14681    });
14682    let multibuffer_item_id = workspace
14683        .update(cx, |workspace, window, cx| {
14684            assert!(
14685                workspace.active_item(cx).is_none(),
14686                "active item should be None before the first item is added"
14687            );
14688            workspace.add_item_to_active_pane(
14689                Box::new(multi_buffer_editor.clone()),
14690                None,
14691                true,
14692                window,
14693                cx,
14694            );
14695            let active_item = workspace
14696                .active_item(cx)
14697                .expect("should have an active item after adding the multi buffer");
14698            assert!(
14699                !active_item.is_singleton(cx),
14700                "A multi buffer was expected to active after adding"
14701            );
14702            active_item.item_id()
14703        })
14704        .unwrap();
14705    cx.executor().run_until_parked();
14706
14707    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14708        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14709            s.select_ranges(Some(1..2))
14710        });
14711        editor.open_excerpts(&OpenExcerpts, window, cx);
14712    });
14713    cx.executor().run_until_parked();
14714    let first_item_id = workspace
14715        .update(cx, |workspace, window, cx| {
14716            let active_item = workspace
14717                .active_item(cx)
14718                .expect("should have an active item after navigating into the 1st buffer");
14719            let first_item_id = active_item.item_id();
14720            assert_ne!(
14721                first_item_id, multibuffer_item_id,
14722                "Should navigate into the 1st buffer and activate it"
14723            );
14724            assert!(
14725                active_item.is_singleton(cx),
14726                "New active item should be a singleton buffer"
14727            );
14728            assert_eq!(
14729                active_item
14730                    .act_as::<Editor>(cx)
14731                    .expect("should have navigated into an editor for the 1st buffer")
14732                    .read(cx)
14733                    .text(cx),
14734                sample_text_1
14735            );
14736
14737            workspace
14738                .go_back(workspace.active_pane().downgrade(), window, cx)
14739                .detach_and_log_err(cx);
14740
14741            first_item_id
14742        })
14743        .unwrap();
14744    cx.executor().run_until_parked();
14745    workspace
14746        .update(cx, |workspace, _, cx| {
14747            let active_item = workspace
14748                .active_item(cx)
14749                .expect("should have an active item after navigating back");
14750            assert_eq!(
14751                active_item.item_id(),
14752                multibuffer_item_id,
14753                "Should navigate back to the multi buffer"
14754            );
14755            assert!(!active_item.is_singleton(cx));
14756        })
14757        .unwrap();
14758
14759    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14760        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14761            s.select_ranges(Some(39..40))
14762        });
14763        editor.open_excerpts(&OpenExcerpts, window, cx);
14764    });
14765    cx.executor().run_until_parked();
14766    let second_item_id = workspace
14767        .update(cx, |workspace, window, cx| {
14768            let active_item = workspace
14769                .active_item(cx)
14770                .expect("should have an active item after navigating into the 2nd buffer");
14771            let second_item_id = active_item.item_id();
14772            assert_ne!(
14773                second_item_id, multibuffer_item_id,
14774                "Should navigate away from the multibuffer"
14775            );
14776            assert_ne!(
14777                second_item_id, first_item_id,
14778                "Should navigate into the 2nd buffer and activate it"
14779            );
14780            assert!(
14781                active_item.is_singleton(cx),
14782                "New active item should be a singleton buffer"
14783            );
14784            assert_eq!(
14785                active_item
14786                    .act_as::<Editor>(cx)
14787                    .expect("should have navigated into an editor")
14788                    .read(cx)
14789                    .text(cx),
14790                sample_text_2
14791            );
14792
14793            workspace
14794                .go_back(workspace.active_pane().downgrade(), window, cx)
14795                .detach_and_log_err(cx);
14796
14797            second_item_id
14798        })
14799        .unwrap();
14800    cx.executor().run_until_parked();
14801    workspace
14802        .update(cx, |workspace, _, cx| {
14803            let active_item = workspace
14804                .active_item(cx)
14805                .expect("should have an active item after navigating back from the 2nd buffer");
14806            assert_eq!(
14807                active_item.item_id(),
14808                multibuffer_item_id,
14809                "Should navigate back from the 2nd buffer to the multi buffer"
14810            );
14811            assert!(!active_item.is_singleton(cx));
14812        })
14813        .unwrap();
14814
14815    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14816        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14817            s.select_ranges(Some(70..70))
14818        });
14819        editor.open_excerpts(&OpenExcerpts, window, cx);
14820    });
14821    cx.executor().run_until_parked();
14822    workspace
14823        .update(cx, |workspace, window, cx| {
14824            let active_item = workspace
14825                .active_item(cx)
14826                .expect("should have an active item after navigating into the 3rd buffer");
14827            let third_item_id = active_item.item_id();
14828            assert_ne!(
14829                third_item_id, multibuffer_item_id,
14830                "Should navigate into the 3rd buffer and activate it"
14831            );
14832            assert_ne!(third_item_id, first_item_id);
14833            assert_ne!(third_item_id, second_item_id);
14834            assert!(
14835                active_item.is_singleton(cx),
14836                "New active item should be a singleton buffer"
14837            );
14838            assert_eq!(
14839                active_item
14840                    .act_as::<Editor>(cx)
14841                    .expect("should have navigated into an editor")
14842                    .read(cx)
14843                    .text(cx),
14844                sample_text_3
14845            );
14846
14847            workspace
14848                .go_back(workspace.active_pane().downgrade(), window, cx)
14849                .detach_and_log_err(cx);
14850        })
14851        .unwrap();
14852    cx.executor().run_until_parked();
14853    workspace
14854        .update(cx, |workspace, _, cx| {
14855            let active_item = workspace
14856                .active_item(cx)
14857                .expect("should have an active item after navigating back from the 3rd buffer");
14858            assert_eq!(
14859                active_item.item_id(),
14860                multibuffer_item_id,
14861                "Should navigate back from the 3rd buffer to the multi buffer"
14862            );
14863            assert!(!active_item.is_singleton(cx));
14864        })
14865        .unwrap();
14866}
14867
14868#[gpui::test]
14869async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14870    init_test(cx, |_| {});
14871
14872    let mut cx = EditorTestContext::new(cx).await;
14873
14874    let diff_base = r#"
14875        use some::mod;
14876
14877        const A: u32 = 42;
14878
14879        fn main() {
14880            println!("hello");
14881
14882            println!("world");
14883        }
14884        "#
14885    .unindent();
14886
14887    cx.set_state(
14888        &r#"
14889        use some::modified;
14890
14891        ˇ
14892        fn main() {
14893            println!("hello there");
14894
14895            println!("around the");
14896            println!("world");
14897        }
14898        "#
14899        .unindent(),
14900    );
14901
14902    cx.set_head_text(&diff_base);
14903    executor.run_until_parked();
14904
14905    cx.update_editor(|editor, window, cx| {
14906        editor.go_to_next_hunk(&GoToHunk, window, cx);
14907        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14908    });
14909    executor.run_until_parked();
14910    cx.assert_state_with_diff(
14911        r#"
14912          use some::modified;
14913
14914
14915          fn main() {
14916        -     println!("hello");
14917        + ˇ    println!("hello there");
14918
14919              println!("around the");
14920              println!("world");
14921          }
14922        "#
14923        .unindent(),
14924    );
14925
14926    cx.update_editor(|editor, window, cx| {
14927        for _ in 0..2 {
14928            editor.go_to_next_hunk(&GoToHunk, window, cx);
14929            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14930        }
14931    });
14932    executor.run_until_parked();
14933    cx.assert_state_with_diff(
14934        r#"
14935        - use some::mod;
14936        + ˇuse some::modified;
14937
14938
14939          fn main() {
14940        -     println!("hello");
14941        +     println!("hello there");
14942
14943        +     println!("around the");
14944              println!("world");
14945          }
14946        "#
14947        .unindent(),
14948    );
14949
14950    cx.update_editor(|editor, window, cx| {
14951        editor.go_to_next_hunk(&GoToHunk, window, cx);
14952        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14953    });
14954    executor.run_until_parked();
14955    cx.assert_state_with_diff(
14956        r#"
14957        - use some::mod;
14958        + use some::modified;
14959
14960        - const A: u32 = 42;
14961          ˇ
14962          fn main() {
14963        -     println!("hello");
14964        +     println!("hello there");
14965
14966        +     println!("around the");
14967              println!("world");
14968          }
14969        "#
14970        .unindent(),
14971    );
14972
14973    cx.update_editor(|editor, window, cx| {
14974        editor.cancel(&Cancel, window, cx);
14975    });
14976
14977    cx.assert_state_with_diff(
14978        r#"
14979          use some::modified;
14980
14981          ˇ
14982          fn main() {
14983              println!("hello there");
14984
14985              println!("around the");
14986              println!("world");
14987          }
14988        "#
14989        .unindent(),
14990    );
14991}
14992
14993#[gpui::test]
14994async fn test_diff_base_change_with_expanded_diff_hunks(
14995    executor: BackgroundExecutor,
14996    cx: &mut TestAppContext,
14997) {
14998    init_test(cx, |_| {});
14999
15000    let mut cx = EditorTestContext::new(cx).await;
15001
15002    let diff_base = r#"
15003        use some::mod1;
15004        use some::mod2;
15005
15006        const A: u32 = 42;
15007        const B: u32 = 42;
15008        const C: u32 = 42;
15009
15010        fn main() {
15011            println!("hello");
15012
15013            println!("world");
15014        }
15015        "#
15016    .unindent();
15017
15018    cx.set_state(
15019        &r#"
15020        use some::mod2;
15021
15022        const A: u32 = 42;
15023        const C: u32 = 42;
15024
15025        fn main(ˇ) {
15026            //println!("hello");
15027
15028            println!("world");
15029            //
15030            //
15031        }
15032        "#
15033        .unindent(),
15034    );
15035
15036    cx.set_head_text(&diff_base);
15037    executor.run_until_parked();
15038
15039    cx.update_editor(|editor, window, cx| {
15040        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15041    });
15042    executor.run_until_parked();
15043    cx.assert_state_with_diff(
15044        r#"
15045        - use some::mod1;
15046          use some::mod2;
15047
15048          const A: u32 = 42;
15049        - const B: u32 = 42;
15050          const C: u32 = 42;
15051
15052          fn main(ˇ) {
15053        -     println!("hello");
15054        +     //println!("hello");
15055
15056              println!("world");
15057        +     //
15058        +     //
15059          }
15060        "#
15061        .unindent(),
15062    );
15063
15064    cx.set_head_text("new diff base!");
15065    executor.run_until_parked();
15066    cx.assert_state_with_diff(
15067        r#"
15068        - new diff base!
15069        + use some::mod2;
15070        +
15071        + const A: u32 = 42;
15072        + const C: u32 = 42;
15073        +
15074        + fn main(ˇ) {
15075        +     //println!("hello");
15076        +
15077        +     println!("world");
15078        +     //
15079        +     //
15080        + }
15081        "#
15082        .unindent(),
15083    );
15084}
15085
15086#[gpui::test]
15087async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15088    init_test(cx, |_| {});
15089
15090    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15091    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15092    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15093    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15094    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15095    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15096
15097    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15098    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15099    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15100
15101    let multi_buffer = cx.new(|cx| {
15102        let mut multibuffer = MultiBuffer::new(ReadWrite);
15103        multibuffer.push_excerpts(
15104            buffer_1.clone(),
15105            [
15106                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15107                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15108                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15109            ],
15110            cx,
15111        );
15112        multibuffer.push_excerpts(
15113            buffer_2.clone(),
15114            [
15115                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15116                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15117                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15118            ],
15119            cx,
15120        );
15121        multibuffer.push_excerpts(
15122            buffer_3.clone(),
15123            [
15124                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15125                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15126                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15127            ],
15128            cx,
15129        );
15130        multibuffer
15131    });
15132
15133    let editor =
15134        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15135    editor
15136        .update(cx, |editor, _window, cx| {
15137            for (buffer, diff_base) in [
15138                (buffer_1.clone(), file_1_old),
15139                (buffer_2.clone(), file_2_old),
15140                (buffer_3.clone(), file_3_old),
15141            ] {
15142                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15143                editor
15144                    .buffer
15145                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15146            }
15147        })
15148        .unwrap();
15149
15150    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15151    cx.run_until_parked();
15152
15153    cx.assert_editor_state(
15154        &"
15155            ˇaaa
15156            ccc
15157            ddd
15158
15159            ggg
15160            hhh
15161
15162
15163            lll
15164            mmm
15165            NNN
15166
15167            qqq
15168            rrr
15169
15170            uuu
15171            111
15172            222
15173            333
15174
15175            666
15176            777
15177
15178            000
15179            !!!"
15180        .unindent(),
15181    );
15182
15183    cx.update_editor(|editor, window, cx| {
15184        editor.select_all(&SelectAll, window, cx);
15185        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15186    });
15187    cx.executor().run_until_parked();
15188
15189    cx.assert_state_with_diff(
15190        "
15191            «aaa
15192          - bbb
15193            ccc
15194            ddd
15195
15196            ggg
15197            hhh
15198
15199
15200            lll
15201            mmm
15202          - nnn
15203          + NNN
15204
15205            qqq
15206            rrr
15207
15208            uuu
15209            111
15210            222
15211            333
15212
15213          + 666
15214            777
15215
15216            000
15217            !!!ˇ»"
15218            .unindent(),
15219    );
15220}
15221
15222#[gpui::test]
15223async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15224    init_test(cx, |_| {});
15225
15226    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15227    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15228
15229    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15230    let multi_buffer = cx.new(|cx| {
15231        let mut multibuffer = MultiBuffer::new(ReadWrite);
15232        multibuffer.push_excerpts(
15233            buffer.clone(),
15234            [
15235                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15236                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15237                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15238            ],
15239            cx,
15240        );
15241        multibuffer
15242    });
15243
15244    let editor =
15245        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15246    editor
15247        .update(cx, |editor, _window, cx| {
15248            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15249            editor
15250                .buffer
15251                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15252        })
15253        .unwrap();
15254
15255    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15256    cx.run_until_parked();
15257
15258    cx.update_editor(|editor, window, cx| {
15259        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15260    });
15261    cx.executor().run_until_parked();
15262
15263    // When the start of a hunk coincides with the start of its excerpt,
15264    // the hunk is expanded. When the start of a a hunk is earlier than
15265    // the start of its excerpt, the hunk is not expanded.
15266    cx.assert_state_with_diff(
15267        "
15268            ˇaaa
15269          - bbb
15270          + BBB
15271
15272          - ddd
15273          - eee
15274          + DDD
15275          + EEE
15276            fff
15277
15278            iii
15279        "
15280        .unindent(),
15281    );
15282}
15283
15284#[gpui::test]
15285async fn test_edits_around_expanded_insertion_hunks(
15286    executor: BackgroundExecutor,
15287    cx: &mut TestAppContext,
15288) {
15289    init_test(cx, |_| {});
15290
15291    let mut cx = EditorTestContext::new(cx).await;
15292
15293    let diff_base = r#"
15294        use some::mod1;
15295        use some::mod2;
15296
15297        const A: u32 = 42;
15298
15299        fn main() {
15300            println!("hello");
15301
15302            println!("world");
15303        }
15304        "#
15305    .unindent();
15306    executor.run_until_parked();
15307    cx.set_state(
15308        &r#"
15309        use some::mod1;
15310        use some::mod2;
15311
15312        const A: u32 = 42;
15313        const B: u32 = 42;
15314        const C: u32 = 42;
15315        ˇ
15316
15317        fn main() {
15318            println!("hello");
15319
15320            println!("world");
15321        }
15322        "#
15323        .unindent(),
15324    );
15325
15326    cx.set_head_text(&diff_base);
15327    executor.run_until_parked();
15328
15329    cx.update_editor(|editor, window, cx| {
15330        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15331    });
15332    executor.run_until_parked();
15333
15334    cx.assert_state_with_diff(
15335        r#"
15336        use some::mod1;
15337        use some::mod2;
15338
15339        const A: u32 = 42;
15340      + const B: u32 = 42;
15341      + const C: u32 = 42;
15342      + ˇ
15343
15344        fn main() {
15345            println!("hello");
15346
15347            println!("world");
15348        }
15349      "#
15350        .unindent(),
15351    );
15352
15353    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15354    executor.run_until_parked();
15355
15356    cx.assert_state_with_diff(
15357        r#"
15358        use some::mod1;
15359        use some::mod2;
15360
15361        const A: u32 = 42;
15362      + const B: u32 = 42;
15363      + const C: u32 = 42;
15364      + const D: u32 = 42;
15365      + ˇ
15366
15367        fn main() {
15368            println!("hello");
15369
15370            println!("world");
15371        }
15372      "#
15373        .unindent(),
15374    );
15375
15376    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15377    executor.run_until_parked();
15378
15379    cx.assert_state_with_diff(
15380        r#"
15381        use some::mod1;
15382        use some::mod2;
15383
15384        const A: u32 = 42;
15385      + const B: u32 = 42;
15386      + const C: u32 = 42;
15387      + const D: u32 = 42;
15388      + const E: u32 = 42;
15389      + ˇ
15390
15391        fn main() {
15392            println!("hello");
15393
15394            println!("world");
15395        }
15396      "#
15397        .unindent(),
15398    );
15399
15400    cx.update_editor(|editor, window, cx| {
15401        editor.delete_line(&DeleteLine, window, cx);
15402    });
15403    executor.run_until_parked();
15404
15405    cx.assert_state_with_diff(
15406        r#"
15407        use some::mod1;
15408        use some::mod2;
15409
15410        const A: u32 = 42;
15411      + const B: u32 = 42;
15412      + const C: u32 = 42;
15413      + const D: u32 = 42;
15414      + const E: u32 = 42;
15415        ˇ
15416        fn main() {
15417            println!("hello");
15418
15419            println!("world");
15420        }
15421      "#
15422        .unindent(),
15423    );
15424
15425    cx.update_editor(|editor, window, cx| {
15426        editor.move_up(&MoveUp, window, cx);
15427        editor.delete_line(&DeleteLine, window, cx);
15428        editor.move_up(&MoveUp, window, cx);
15429        editor.delete_line(&DeleteLine, window, cx);
15430        editor.move_up(&MoveUp, window, cx);
15431        editor.delete_line(&DeleteLine, window, cx);
15432    });
15433    executor.run_until_parked();
15434    cx.assert_state_with_diff(
15435        r#"
15436        use some::mod1;
15437        use some::mod2;
15438
15439        const A: u32 = 42;
15440      + const B: u32 = 42;
15441        ˇ
15442        fn main() {
15443            println!("hello");
15444
15445            println!("world");
15446        }
15447      "#
15448        .unindent(),
15449    );
15450
15451    cx.update_editor(|editor, window, cx| {
15452        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15453        editor.delete_line(&DeleteLine, window, cx);
15454    });
15455    executor.run_until_parked();
15456    cx.assert_state_with_diff(
15457        r#"
15458        ˇ
15459        fn main() {
15460            println!("hello");
15461
15462            println!("world");
15463        }
15464      "#
15465        .unindent(),
15466    );
15467}
15468
15469#[gpui::test]
15470async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15471    init_test(cx, |_| {});
15472
15473    let mut cx = EditorTestContext::new(cx).await;
15474    cx.set_head_text(indoc! { "
15475        one
15476        two
15477        three
15478        four
15479        five
15480        "
15481    });
15482    cx.set_state(indoc! { "
15483        one
15484        ˇthree
15485        five
15486    "});
15487    cx.run_until_parked();
15488    cx.update_editor(|editor, window, cx| {
15489        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15490    });
15491    cx.assert_state_with_diff(
15492        indoc! { "
15493        one
15494      - two
15495        ˇthree
15496      - four
15497        five
15498    "}
15499        .to_string(),
15500    );
15501    cx.update_editor(|editor, window, cx| {
15502        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15503    });
15504
15505    cx.assert_state_with_diff(
15506        indoc! { "
15507        one
15508        ˇthree
15509        five
15510    "}
15511        .to_string(),
15512    );
15513
15514    cx.set_state(indoc! { "
15515        one
15516        ˇTWO
15517        three
15518        four
15519        five
15520    "});
15521    cx.run_until_parked();
15522    cx.update_editor(|editor, window, cx| {
15523        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15524    });
15525
15526    cx.assert_state_with_diff(
15527        indoc! { "
15528            one
15529          - two
15530          + ˇTWO
15531            three
15532            four
15533            five
15534        "}
15535        .to_string(),
15536    );
15537    cx.update_editor(|editor, window, cx| {
15538        editor.move_up(&Default::default(), window, cx);
15539        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15540    });
15541    cx.assert_state_with_diff(
15542        indoc! { "
15543            one
15544            ˇTWO
15545            three
15546            four
15547            five
15548        "}
15549        .to_string(),
15550    );
15551}
15552
15553#[gpui::test]
15554async fn test_edits_around_expanded_deletion_hunks(
15555    executor: BackgroundExecutor,
15556    cx: &mut TestAppContext,
15557) {
15558    init_test(cx, |_| {});
15559
15560    let mut cx = EditorTestContext::new(cx).await;
15561
15562    let diff_base = r#"
15563        use some::mod1;
15564        use some::mod2;
15565
15566        const A: u32 = 42;
15567        const B: u32 = 42;
15568        const C: u32 = 42;
15569
15570
15571        fn main() {
15572            println!("hello");
15573
15574            println!("world");
15575        }
15576    "#
15577    .unindent();
15578    executor.run_until_parked();
15579    cx.set_state(
15580        &r#"
15581        use some::mod1;
15582        use some::mod2;
15583
15584        ˇconst B: u32 = 42;
15585        const C: u32 = 42;
15586
15587
15588        fn main() {
15589            println!("hello");
15590
15591            println!("world");
15592        }
15593        "#
15594        .unindent(),
15595    );
15596
15597    cx.set_head_text(&diff_base);
15598    executor.run_until_parked();
15599
15600    cx.update_editor(|editor, window, cx| {
15601        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15602    });
15603    executor.run_until_parked();
15604
15605    cx.assert_state_with_diff(
15606        r#"
15607        use some::mod1;
15608        use some::mod2;
15609
15610      - const A: u32 = 42;
15611        ˇconst B: u32 = 42;
15612        const C: u32 = 42;
15613
15614
15615        fn main() {
15616            println!("hello");
15617
15618            println!("world");
15619        }
15620      "#
15621        .unindent(),
15622    );
15623
15624    cx.update_editor(|editor, window, cx| {
15625        editor.delete_line(&DeleteLine, window, cx);
15626    });
15627    executor.run_until_parked();
15628    cx.assert_state_with_diff(
15629        r#"
15630        use some::mod1;
15631        use some::mod2;
15632
15633      - const A: u32 = 42;
15634      - const B: u32 = 42;
15635        ˇconst C: u32 = 42;
15636
15637
15638        fn main() {
15639            println!("hello");
15640
15641            println!("world");
15642        }
15643      "#
15644        .unindent(),
15645    );
15646
15647    cx.update_editor(|editor, window, cx| {
15648        editor.delete_line(&DeleteLine, window, cx);
15649    });
15650    executor.run_until_parked();
15651    cx.assert_state_with_diff(
15652        r#"
15653        use some::mod1;
15654        use some::mod2;
15655
15656      - const A: u32 = 42;
15657      - const B: u32 = 42;
15658      - const C: u32 = 42;
15659        ˇ
15660
15661        fn main() {
15662            println!("hello");
15663
15664            println!("world");
15665        }
15666      "#
15667        .unindent(),
15668    );
15669
15670    cx.update_editor(|editor, window, cx| {
15671        editor.handle_input("replacement", window, cx);
15672    });
15673    executor.run_until_parked();
15674    cx.assert_state_with_diff(
15675        r#"
15676        use some::mod1;
15677        use some::mod2;
15678
15679      - const A: u32 = 42;
15680      - const B: u32 = 42;
15681      - const C: u32 = 42;
15682      -
15683      + replacementˇ
15684
15685        fn main() {
15686            println!("hello");
15687
15688            println!("world");
15689        }
15690      "#
15691        .unindent(),
15692    );
15693}
15694
15695#[gpui::test]
15696async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15697    init_test(cx, |_| {});
15698
15699    let mut cx = EditorTestContext::new(cx).await;
15700
15701    let base_text = r#"
15702        one
15703        two
15704        three
15705        four
15706        five
15707    "#
15708    .unindent();
15709    executor.run_until_parked();
15710    cx.set_state(
15711        &r#"
15712        one
15713        two
15714        fˇour
15715        five
15716        "#
15717        .unindent(),
15718    );
15719
15720    cx.set_head_text(&base_text);
15721    executor.run_until_parked();
15722
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          one
15731          two
15732        - three
15733          fˇour
15734          five
15735        "#
15736        .unindent(),
15737    );
15738
15739    cx.update_editor(|editor, window, cx| {
15740        editor.backspace(&Backspace, window, cx);
15741        editor.backspace(&Backspace, window, cx);
15742    });
15743    executor.run_until_parked();
15744    cx.assert_state_with_diff(
15745        r#"
15746          one
15747          two
15748        - threeˇ
15749        - four
15750        + our
15751          five
15752        "#
15753        .unindent(),
15754    );
15755}
15756
15757#[gpui::test]
15758async fn test_edit_after_expanded_modification_hunk(
15759    executor: BackgroundExecutor,
15760    cx: &mut TestAppContext,
15761) {
15762    init_test(cx, |_| {});
15763
15764    let mut cx = EditorTestContext::new(cx).await;
15765
15766    let diff_base = r#"
15767        use some::mod1;
15768        use some::mod2;
15769
15770        const A: u32 = 42;
15771        const B: u32 = 42;
15772        const C: u32 = 42;
15773        const D: u32 = 42;
15774
15775
15776        fn main() {
15777            println!("hello");
15778
15779            println!("world");
15780        }"#
15781    .unindent();
15782
15783    cx.set_state(
15784        &r#"
15785        use some::mod1;
15786        use some::mod2;
15787
15788        const A: u32 = 42;
15789        const B: u32 = 42;
15790        const C: u32 = 43ˇ
15791        const D: u32 = 42;
15792
15793
15794        fn main() {
15795            println!("hello");
15796
15797            println!("world");
15798        }"#
15799        .unindent(),
15800    );
15801
15802    cx.set_head_text(&diff_base);
15803    executor.run_until_parked();
15804    cx.update_editor(|editor, window, cx| {
15805        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15806    });
15807    executor.run_until_parked();
15808
15809    cx.assert_state_with_diff(
15810        r#"
15811        use some::mod1;
15812        use some::mod2;
15813
15814        const A: u32 = 42;
15815        const B: u32 = 42;
15816      - const C: u32 = 42;
15817      + const C: u32 = 43ˇ
15818        const D: u32 = 42;
15819
15820
15821        fn main() {
15822            println!("hello");
15823
15824            println!("world");
15825        }"#
15826        .unindent(),
15827    );
15828
15829    cx.update_editor(|editor, window, cx| {
15830        editor.handle_input("\nnew_line\n", window, cx);
15831    });
15832    executor.run_until_parked();
15833
15834    cx.assert_state_with_diff(
15835        r#"
15836        use some::mod1;
15837        use some::mod2;
15838
15839        const A: u32 = 42;
15840        const B: u32 = 42;
15841      - const C: u32 = 42;
15842      + const C: u32 = 43
15843      + new_line
15844      + ˇ
15845        const D: u32 = 42;
15846
15847
15848        fn main() {
15849            println!("hello");
15850
15851            println!("world");
15852        }"#
15853        .unindent(),
15854    );
15855}
15856
15857#[gpui::test]
15858async fn test_stage_and_unstage_added_file_hunk(
15859    executor: BackgroundExecutor,
15860    cx: &mut TestAppContext,
15861) {
15862    init_test(cx, |_| {});
15863
15864    let mut cx = EditorTestContext::new(cx).await;
15865    cx.update_editor(|editor, _, cx| {
15866        editor.set_expand_all_diff_hunks(cx);
15867    });
15868
15869    let working_copy = r#"
15870            ˇfn main() {
15871                println!("hello, world!");
15872            }
15873        "#
15874    .unindent();
15875
15876    cx.set_state(&working_copy);
15877    executor.run_until_parked();
15878
15879    cx.assert_state_with_diff(
15880        r#"
15881            + ˇfn main() {
15882            +     println!("hello, world!");
15883            + }
15884        "#
15885        .unindent(),
15886    );
15887    cx.assert_index_text(None);
15888
15889    cx.update_editor(|editor, window, cx| {
15890        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15891    });
15892    executor.run_until_parked();
15893    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15894    cx.assert_state_with_diff(
15895        r#"
15896            + ˇfn main() {
15897            +     println!("hello, world!");
15898            + }
15899        "#
15900        .unindent(),
15901    );
15902
15903    cx.update_editor(|editor, window, cx| {
15904        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15905    });
15906    executor.run_until_parked();
15907    cx.assert_index_text(None);
15908}
15909
15910async fn setup_indent_guides_editor(
15911    text: &str,
15912    cx: &mut TestAppContext,
15913) -> (BufferId, EditorTestContext) {
15914    init_test(cx, |_| {});
15915
15916    let mut cx = EditorTestContext::new(cx).await;
15917
15918    let buffer_id = cx.update_editor(|editor, window, cx| {
15919        editor.set_text(text, window, cx);
15920        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15921
15922        buffer_ids[0]
15923    });
15924
15925    (buffer_id, cx)
15926}
15927
15928fn assert_indent_guides(
15929    range: Range<u32>,
15930    expected: Vec<IndentGuide>,
15931    active_indices: Option<Vec<usize>>,
15932    cx: &mut EditorTestContext,
15933) {
15934    let indent_guides = cx.update_editor(|editor, window, cx| {
15935        let snapshot = editor.snapshot(window, cx).display_snapshot;
15936        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15937            editor,
15938            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15939            true,
15940            &snapshot,
15941            cx,
15942        );
15943
15944        indent_guides.sort_by(|a, b| {
15945            a.depth.cmp(&b.depth).then(
15946                a.start_row
15947                    .cmp(&b.start_row)
15948                    .then(a.end_row.cmp(&b.end_row)),
15949            )
15950        });
15951        indent_guides
15952    });
15953
15954    if let Some(expected) = active_indices {
15955        let active_indices = cx.update_editor(|editor, window, cx| {
15956            let snapshot = editor.snapshot(window, cx).display_snapshot;
15957            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15958        });
15959
15960        assert_eq!(
15961            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15962            expected,
15963            "Active indent guide indices do not match"
15964        );
15965    }
15966
15967    assert_eq!(indent_guides, expected, "Indent guides do not match");
15968}
15969
15970fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15971    IndentGuide {
15972        buffer_id,
15973        start_row: MultiBufferRow(start_row),
15974        end_row: MultiBufferRow(end_row),
15975        depth,
15976        tab_size: 4,
15977        settings: IndentGuideSettings {
15978            enabled: true,
15979            line_width: 1,
15980            active_line_width: 1,
15981            ..Default::default()
15982        },
15983    }
15984}
15985
15986#[gpui::test]
15987async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15988    let (buffer_id, mut cx) = setup_indent_guides_editor(
15989        &"
15990    fn main() {
15991        let a = 1;
15992    }"
15993        .unindent(),
15994        cx,
15995    )
15996    .await;
15997
15998    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15999}
16000
16001#[gpui::test]
16002async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16003    let (buffer_id, mut cx) = setup_indent_guides_editor(
16004        &"
16005    fn main() {
16006        let a = 1;
16007        let b = 2;
16008    }"
16009        .unindent(),
16010        cx,
16011    )
16012    .await;
16013
16014    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16015}
16016
16017#[gpui::test]
16018async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16019    let (buffer_id, mut cx) = setup_indent_guides_editor(
16020        &"
16021    fn main() {
16022        let a = 1;
16023        if a == 3 {
16024            let b = 2;
16025        } else {
16026            let c = 3;
16027        }
16028    }"
16029        .unindent(),
16030        cx,
16031    )
16032    .await;
16033
16034    assert_indent_guides(
16035        0..8,
16036        vec![
16037            indent_guide(buffer_id, 1, 6, 0),
16038            indent_guide(buffer_id, 3, 3, 1),
16039            indent_guide(buffer_id, 5, 5, 1),
16040        ],
16041        None,
16042        &mut cx,
16043    );
16044}
16045
16046#[gpui::test]
16047async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16048    let (buffer_id, mut cx) = setup_indent_guides_editor(
16049        &"
16050    fn main() {
16051        let a = 1;
16052            let b = 2;
16053        let c = 3;
16054    }"
16055        .unindent(),
16056        cx,
16057    )
16058    .await;
16059
16060    assert_indent_guides(
16061        0..5,
16062        vec![
16063            indent_guide(buffer_id, 1, 3, 0),
16064            indent_guide(buffer_id, 2, 2, 1),
16065        ],
16066        None,
16067        &mut cx,
16068    );
16069}
16070
16071#[gpui::test]
16072async fn test_indent_guide_continues_on_empty_line(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        .unindent(),
16081        cx,
16082    )
16083    .await;
16084
16085    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16086}
16087
16088#[gpui::test]
16089async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16090    let (buffer_id, mut cx) = setup_indent_guides_editor(
16091        &"
16092        fn main() {
16093            let a = 1;
16094
16095            let c = 3;
16096
16097            if a == 3 {
16098                let b = 2;
16099            } else {
16100                let c = 3;
16101            }
16102        }"
16103        .unindent(),
16104        cx,
16105    )
16106    .await;
16107
16108    assert_indent_guides(
16109        0..11,
16110        vec![
16111            indent_guide(buffer_id, 1, 9, 0),
16112            indent_guide(buffer_id, 6, 6, 1),
16113            indent_guide(buffer_id, 8, 8, 1),
16114        ],
16115        None,
16116        &mut cx,
16117    );
16118}
16119
16120#[gpui::test]
16121async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16122    let (buffer_id, mut cx) = setup_indent_guides_editor(
16123        &"
16124        fn main() {
16125            let a = 1;
16126
16127            let c = 3;
16128
16129            if a == 3 {
16130                let b = 2;
16131            } else {
16132                let c = 3;
16133            }
16134        }"
16135        .unindent(),
16136        cx,
16137    )
16138    .await;
16139
16140    assert_indent_guides(
16141        1..11,
16142        vec![
16143            indent_guide(buffer_id, 1, 9, 0),
16144            indent_guide(buffer_id, 6, 6, 1),
16145            indent_guide(buffer_id, 8, 8, 1),
16146        ],
16147        None,
16148        &mut cx,
16149    );
16150}
16151
16152#[gpui::test]
16153async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16154    let (buffer_id, mut cx) = setup_indent_guides_editor(
16155        &"
16156        fn main() {
16157            let a = 1;
16158
16159            let c = 3;
16160
16161            if a == 3 {
16162                let b = 2;
16163            } else {
16164                let c = 3;
16165            }
16166        }"
16167        .unindent(),
16168        cx,
16169    )
16170    .await;
16171
16172    assert_indent_guides(
16173        1..10,
16174        vec![
16175            indent_guide(buffer_id, 1, 9, 0),
16176            indent_guide(buffer_id, 6, 6, 1),
16177            indent_guide(buffer_id, 8, 8, 1),
16178        ],
16179        None,
16180        &mut cx,
16181    );
16182}
16183
16184#[gpui::test]
16185async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16186    let (buffer_id, mut cx) = setup_indent_guides_editor(
16187        &"
16188        block1
16189            block2
16190                block3
16191                    block4
16192            block2
16193        block1
16194        block1"
16195            .unindent(),
16196        cx,
16197    )
16198    .await;
16199
16200    assert_indent_guides(
16201        1..10,
16202        vec![
16203            indent_guide(buffer_id, 1, 4, 0),
16204            indent_guide(buffer_id, 2, 3, 1),
16205            indent_guide(buffer_id, 3, 3, 2),
16206        ],
16207        None,
16208        &mut cx,
16209    );
16210}
16211
16212#[gpui::test]
16213async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16214    let (buffer_id, mut cx) = setup_indent_guides_editor(
16215        &"
16216        block1
16217            block2
16218                block3
16219
16220        block1
16221        block1"
16222            .unindent(),
16223        cx,
16224    )
16225    .await;
16226
16227    assert_indent_guides(
16228        0..6,
16229        vec![
16230            indent_guide(buffer_id, 1, 2, 0),
16231            indent_guide(buffer_id, 2, 2, 1),
16232        ],
16233        None,
16234        &mut cx,
16235    );
16236}
16237
16238#[gpui::test]
16239async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16240    let (buffer_id, mut cx) = setup_indent_guides_editor(
16241        &"
16242        block1
16243
16244
16245
16246            block2
16247        "
16248        .unindent(),
16249        cx,
16250    )
16251    .await;
16252
16253    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16254}
16255
16256#[gpui::test]
16257async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16258    let (buffer_id, mut cx) = setup_indent_guides_editor(
16259        &"
16260        def a:
16261        \tb = 3
16262        \tif True:
16263        \t\tc = 4
16264        \t\td = 5
16265        \tprint(b)
16266        "
16267        .unindent(),
16268        cx,
16269    )
16270    .await;
16271
16272    assert_indent_guides(
16273        0..6,
16274        vec![
16275            indent_guide(buffer_id, 1, 6, 0),
16276            indent_guide(buffer_id, 3, 4, 1),
16277        ],
16278        None,
16279        &mut cx,
16280    );
16281}
16282
16283#[gpui::test]
16284async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16285    let (buffer_id, mut cx) = setup_indent_guides_editor(
16286        &"
16287    fn main() {
16288        let a = 1;
16289    }"
16290        .unindent(),
16291        cx,
16292    )
16293    .await;
16294
16295    cx.update_editor(|editor, window, cx| {
16296        editor.change_selections(None, window, cx, |s| {
16297            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16298        });
16299    });
16300
16301    assert_indent_guides(
16302        0..3,
16303        vec![indent_guide(buffer_id, 1, 1, 0)],
16304        Some(vec![0]),
16305        &mut cx,
16306    );
16307}
16308
16309#[gpui::test]
16310async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16311    let (buffer_id, mut cx) = setup_indent_guides_editor(
16312        &"
16313    fn main() {
16314        if 1 == 2 {
16315            let a = 1;
16316        }
16317    }"
16318        .unindent(),
16319        cx,
16320    )
16321    .await;
16322
16323    cx.update_editor(|editor, window, cx| {
16324        editor.change_selections(None, window, cx, |s| {
16325            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16326        });
16327    });
16328
16329    assert_indent_guides(
16330        0..4,
16331        vec![
16332            indent_guide(buffer_id, 1, 3, 0),
16333            indent_guide(buffer_id, 2, 2, 1),
16334        ],
16335        Some(vec![1]),
16336        &mut cx,
16337    );
16338
16339    cx.update_editor(|editor, window, cx| {
16340        editor.change_selections(None, window, cx, |s| {
16341            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16342        });
16343    });
16344
16345    assert_indent_guides(
16346        0..4,
16347        vec![
16348            indent_guide(buffer_id, 1, 3, 0),
16349            indent_guide(buffer_id, 2, 2, 1),
16350        ],
16351        Some(vec![1]),
16352        &mut cx,
16353    );
16354
16355    cx.update_editor(|editor, window, cx| {
16356        editor.change_selections(None, window, cx, |s| {
16357            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16358        });
16359    });
16360
16361    assert_indent_guides(
16362        0..4,
16363        vec![
16364            indent_guide(buffer_id, 1, 3, 0),
16365            indent_guide(buffer_id, 2, 2, 1),
16366        ],
16367        Some(vec![0]),
16368        &mut cx,
16369    );
16370}
16371
16372#[gpui::test]
16373async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16374    let (buffer_id, mut cx) = setup_indent_guides_editor(
16375        &"
16376    fn main() {
16377        let a = 1;
16378
16379        let b = 2;
16380    }"
16381        .unindent(),
16382        cx,
16383    )
16384    .await;
16385
16386    cx.update_editor(|editor, window, cx| {
16387        editor.change_selections(None, window, cx, |s| {
16388            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16389        });
16390    });
16391
16392    assert_indent_guides(
16393        0..5,
16394        vec![indent_guide(buffer_id, 1, 3, 0)],
16395        Some(vec![0]),
16396        &mut cx,
16397    );
16398}
16399
16400#[gpui::test]
16401async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16402    let (buffer_id, mut cx) = setup_indent_guides_editor(
16403        &"
16404    def m:
16405        a = 1
16406        pass"
16407            .unindent(),
16408        cx,
16409    )
16410    .await;
16411
16412    cx.update_editor(|editor, window, cx| {
16413        editor.change_selections(None, window, cx, |s| {
16414            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16415        });
16416    });
16417
16418    assert_indent_guides(
16419        0..3,
16420        vec![indent_guide(buffer_id, 1, 2, 0)],
16421        Some(vec![0]),
16422        &mut cx,
16423    );
16424}
16425
16426#[gpui::test]
16427async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16428    init_test(cx, |_| {});
16429    let mut cx = EditorTestContext::new(cx).await;
16430    let text = indoc! {
16431        "
16432        impl A {
16433            fn b() {
16434                0;
16435                3;
16436                5;
16437                6;
16438                7;
16439            }
16440        }
16441        "
16442    };
16443    let base_text = indoc! {
16444        "
16445        impl A {
16446            fn b() {
16447                0;
16448                1;
16449                2;
16450                3;
16451                4;
16452            }
16453            fn c() {
16454                5;
16455                6;
16456                7;
16457            }
16458        }
16459        "
16460    };
16461
16462    cx.update_editor(|editor, window, cx| {
16463        editor.set_text(text, window, cx);
16464
16465        editor.buffer().update(cx, |multibuffer, cx| {
16466            let buffer = multibuffer.as_singleton().unwrap();
16467            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16468
16469            multibuffer.set_all_diff_hunks_expanded(cx);
16470            multibuffer.add_diff(diff, cx);
16471
16472            buffer.read(cx).remote_id()
16473        })
16474    });
16475    cx.run_until_parked();
16476
16477    cx.assert_state_with_diff(
16478        indoc! { "
16479          impl A {
16480              fn b() {
16481                  0;
16482        -         1;
16483        -         2;
16484                  3;
16485        -         4;
16486        -     }
16487        -     fn c() {
16488                  5;
16489                  6;
16490                  7;
16491              }
16492          }
16493          ˇ"
16494        }
16495        .to_string(),
16496    );
16497
16498    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16499        editor
16500            .snapshot(window, cx)
16501            .buffer_snapshot
16502            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16503            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16504            .collect::<Vec<_>>()
16505    });
16506    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16507    assert_eq!(
16508        actual_guides,
16509        vec![
16510            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16511            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16512            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16513        ]
16514    );
16515}
16516
16517#[gpui::test]
16518async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16519    init_test(cx, |_| {});
16520    let mut cx = EditorTestContext::new(cx).await;
16521
16522    let diff_base = r#"
16523        a
16524        b
16525        c
16526        "#
16527    .unindent();
16528
16529    cx.set_state(
16530        &r#"
16531        ˇA
16532        b
16533        C
16534        "#
16535        .unindent(),
16536    );
16537    cx.set_head_text(&diff_base);
16538    cx.update_editor(|editor, window, cx| {
16539        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16540    });
16541    executor.run_until_parked();
16542
16543    let both_hunks_expanded = r#"
16544        - a
16545        + ˇA
16546          b
16547        - c
16548        + C
16549        "#
16550    .unindent();
16551
16552    cx.assert_state_with_diff(both_hunks_expanded.clone());
16553
16554    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16555        let snapshot = editor.snapshot(window, cx);
16556        let hunks = editor
16557            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16558            .collect::<Vec<_>>();
16559        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16560        let buffer_id = hunks[0].buffer_id;
16561        hunks
16562            .into_iter()
16563            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16564            .collect::<Vec<_>>()
16565    });
16566    assert_eq!(hunk_ranges.len(), 2);
16567
16568    cx.update_editor(|editor, _, cx| {
16569        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16570    });
16571    executor.run_until_parked();
16572
16573    let second_hunk_expanded = r#"
16574          ˇA
16575          b
16576        - c
16577        + C
16578        "#
16579    .unindent();
16580
16581    cx.assert_state_with_diff(second_hunk_expanded);
16582
16583    cx.update_editor(|editor, _, cx| {
16584        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16585    });
16586    executor.run_until_parked();
16587
16588    cx.assert_state_with_diff(both_hunks_expanded.clone());
16589
16590    cx.update_editor(|editor, _, cx| {
16591        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16592    });
16593    executor.run_until_parked();
16594
16595    let first_hunk_expanded = r#"
16596        - a
16597        + ˇA
16598          b
16599          C
16600        "#
16601    .unindent();
16602
16603    cx.assert_state_with_diff(first_hunk_expanded);
16604
16605    cx.update_editor(|editor, _, cx| {
16606        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16607    });
16608    executor.run_until_parked();
16609
16610    cx.assert_state_with_diff(both_hunks_expanded);
16611
16612    cx.set_state(
16613        &r#"
16614        ˇA
16615        b
16616        "#
16617        .unindent(),
16618    );
16619    cx.run_until_parked();
16620
16621    // TODO this cursor position seems bad
16622    cx.assert_state_with_diff(
16623        r#"
16624        - ˇa
16625        + A
16626          b
16627        "#
16628        .unindent(),
16629    );
16630
16631    cx.update_editor(|editor, window, cx| {
16632        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16633    });
16634
16635    cx.assert_state_with_diff(
16636        r#"
16637            - ˇa
16638            + A
16639              b
16640            - c
16641            "#
16642        .unindent(),
16643    );
16644
16645    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16646        let snapshot = editor.snapshot(window, cx);
16647        let hunks = editor
16648            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16649            .collect::<Vec<_>>();
16650        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16651        let buffer_id = hunks[0].buffer_id;
16652        hunks
16653            .into_iter()
16654            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16655            .collect::<Vec<_>>()
16656    });
16657    assert_eq!(hunk_ranges.len(), 2);
16658
16659    cx.update_editor(|editor, _, cx| {
16660        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16661    });
16662    executor.run_until_parked();
16663
16664    cx.assert_state_with_diff(
16665        r#"
16666        - ˇa
16667        + A
16668          b
16669        "#
16670        .unindent(),
16671    );
16672}
16673
16674#[gpui::test]
16675async fn test_toggle_deletion_hunk_at_start_of_file(
16676    executor: BackgroundExecutor,
16677    cx: &mut TestAppContext,
16678) {
16679    init_test(cx, |_| {});
16680    let mut cx = EditorTestContext::new(cx).await;
16681
16682    let diff_base = r#"
16683        a
16684        b
16685        c
16686        "#
16687    .unindent();
16688
16689    cx.set_state(
16690        &r#"
16691        ˇb
16692        c
16693        "#
16694        .unindent(),
16695    );
16696    cx.set_head_text(&diff_base);
16697    cx.update_editor(|editor, window, cx| {
16698        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16699    });
16700    executor.run_until_parked();
16701
16702    let hunk_expanded = r#"
16703        - a
16704          ˇb
16705          c
16706        "#
16707    .unindent();
16708
16709    cx.assert_state_with_diff(hunk_expanded.clone());
16710
16711    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16712        let snapshot = editor.snapshot(window, cx);
16713        let hunks = editor
16714            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16715            .collect::<Vec<_>>();
16716        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16717        let buffer_id = hunks[0].buffer_id;
16718        hunks
16719            .into_iter()
16720            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16721            .collect::<Vec<_>>()
16722    });
16723    assert_eq!(hunk_ranges.len(), 1);
16724
16725    cx.update_editor(|editor, _, cx| {
16726        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16727    });
16728    executor.run_until_parked();
16729
16730    let hunk_collapsed = r#"
16731          ˇb
16732          c
16733        "#
16734    .unindent();
16735
16736    cx.assert_state_with_diff(hunk_collapsed);
16737
16738    cx.update_editor(|editor, _, cx| {
16739        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16740    });
16741    executor.run_until_parked();
16742
16743    cx.assert_state_with_diff(hunk_expanded.clone());
16744}
16745
16746#[gpui::test]
16747async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16748    init_test(cx, |_| {});
16749
16750    let fs = FakeFs::new(cx.executor());
16751    fs.insert_tree(
16752        path!("/test"),
16753        json!({
16754            ".git": {},
16755            "file-1": "ONE\n",
16756            "file-2": "TWO\n",
16757            "file-3": "THREE\n",
16758        }),
16759    )
16760    .await;
16761
16762    fs.set_head_for_repo(
16763        path!("/test/.git").as_ref(),
16764        &[
16765            ("file-1".into(), "one\n".into()),
16766            ("file-2".into(), "two\n".into()),
16767            ("file-3".into(), "three\n".into()),
16768        ],
16769    );
16770
16771    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16772    let mut buffers = vec![];
16773    for i in 1..=3 {
16774        let buffer = project
16775            .update(cx, |project, cx| {
16776                let path = format!(path!("/test/file-{}"), i);
16777                project.open_local_buffer(path, cx)
16778            })
16779            .await
16780            .unwrap();
16781        buffers.push(buffer);
16782    }
16783
16784    let multibuffer = cx.new(|cx| {
16785        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16786        multibuffer.set_all_diff_hunks_expanded(cx);
16787        for buffer in &buffers {
16788            let snapshot = buffer.read(cx).snapshot();
16789            multibuffer.set_excerpts_for_path(
16790                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16791                buffer.clone(),
16792                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16793                DEFAULT_MULTIBUFFER_CONTEXT,
16794                cx,
16795            );
16796        }
16797        multibuffer
16798    });
16799
16800    let editor = cx.add_window(|window, cx| {
16801        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16802    });
16803    cx.run_until_parked();
16804
16805    let snapshot = editor
16806        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16807        .unwrap();
16808    let hunks = snapshot
16809        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16810        .map(|hunk| match hunk {
16811            DisplayDiffHunk::Unfolded {
16812                display_row_range, ..
16813            } => display_row_range,
16814            DisplayDiffHunk::Folded { .. } => unreachable!(),
16815        })
16816        .collect::<Vec<_>>();
16817    assert_eq!(
16818        hunks,
16819        [
16820            DisplayRow(2)..DisplayRow(4),
16821            DisplayRow(7)..DisplayRow(9),
16822            DisplayRow(12)..DisplayRow(14),
16823        ]
16824    );
16825}
16826
16827#[gpui::test]
16828async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16829    init_test(cx, |_| {});
16830
16831    let mut cx = EditorTestContext::new(cx).await;
16832    cx.set_head_text(indoc! { "
16833        one
16834        two
16835        three
16836        four
16837        five
16838        "
16839    });
16840    cx.set_index_text(indoc! { "
16841        one
16842        two
16843        three
16844        four
16845        five
16846        "
16847    });
16848    cx.set_state(indoc! {"
16849        one
16850        TWO
16851        ˇTHREE
16852        FOUR
16853        five
16854    "});
16855    cx.run_until_parked();
16856    cx.update_editor(|editor, window, cx| {
16857        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16858    });
16859    cx.run_until_parked();
16860    cx.assert_index_text(Some(indoc! {"
16861        one
16862        TWO
16863        THREE
16864        FOUR
16865        five
16866    "}));
16867    cx.set_state(indoc! { "
16868        one
16869        TWO
16870        ˇTHREE-HUNDRED
16871        FOUR
16872        five
16873    "});
16874    cx.run_until_parked();
16875    cx.update_editor(|editor, window, cx| {
16876        let snapshot = editor.snapshot(window, cx);
16877        let hunks = editor
16878            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16879            .collect::<Vec<_>>();
16880        assert_eq!(hunks.len(), 1);
16881        assert_eq!(
16882            hunks[0].status(),
16883            DiffHunkStatus {
16884                kind: DiffHunkStatusKind::Modified,
16885                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16886            }
16887        );
16888
16889        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16890    });
16891    cx.run_until_parked();
16892    cx.assert_index_text(Some(indoc! {"
16893        one
16894        TWO
16895        THREE-HUNDRED
16896        FOUR
16897        five
16898    "}));
16899}
16900
16901#[gpui::test]
16902fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16903    init_test(cx, |_| {});
16904
16905    let editor = cx.add_window(|window, cx| {
16906        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16907        build_editor(buffer, window, cx)
16908    });
16909
16910    let render_args = Arc::new(Mutex::new(None));
16911    let snapshot = editor
16912        .update(cx, |editor, window, cx| {
16913            let snapshot = editor.buffer().read(cx).snapshot(cx);
16914            let range =
16915                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16916
16917            struct RenderArgs {
16918                row: MultiBufferRow,
16919                folded: bool,
16920                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16921            }
16922
16923            let crease = Crease::inline(
16924                range,
16925                FoldPlaceholder::test(),
16926                {
16927                    let toggle_callback = render_args.clone();
16928                    move |row, folded, callback, _window, _cx| {
16929                        *toggle_callback.lock() = Some(RenderArgs {
16930                            row,
16931                            folded,
16932                            callback,
16933                        });
16934                        div()
16935                    }
16936                },
16937                |_row, _folded, _window, _cx| div(),
16938            );
16939
16940            editor.insert_creases(Some(crease), cx);
16941            let snapshot = editor.snapshot(window, cx);
16942            let _div = snapshot.render_crease_toggle(
16943                MultiBufferRow(1),
16944                false,
16945                cx.entity().clone(),
16946                window,
16947                cx,
16948            );
16949            snapshot
16950        })
16951        .unwrap();
16952
16953    let render_args = render_args.lock().take().unwrap();
16954    assert_eq!(render_args.row, MultiBufferRow(1));
16955    assert!(!render_args.folded);
16956    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16957
16958    cx.update_window(*editor, |_, window, cx| {
16959        (render_args.callback)(true, window, cx)
16960    })
16961    .unwrap();
16962    let snapshot = editor
16963        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16964        .unwrap();
16965    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16966
16967    cx.update_window(*editor, |_, window, cx| {
16968        (render_args.callback)(false, window, cx)
16969    })
16970    .unwrap();
16971    let snapshot = editor
16972        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16973        .unwrap();
16974    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16975}
16976
16977#[gpui::test]
16978async fn test_input_text(cx: &mut TestAppContext) {
16979    init_test(cx, |_| {});
16980    let mut cx = EditorTestContext::new(cx).await;
16981
16982    cx.set_state(
16983        &r#"ˇone
16984        two
16985
16986        three
16987        fourˇ
16988        five
16989
16990        siˇx"#
16991            .unindent(),
16992    );
16993
16994    cx.dispatch_action(HandleInput(String::new()));
16995    cx.assert_editor_state(
16996        &r#"ˇone
16997        two
16998
16999        three
17000        fourˇ
17001        five
17002
17003        siˇx"#
17004            .unindent(),
17005    );
17006
17007    cx.dispatch_action(HandleInput("AAAA".to_string()));
17008    cx.assert_editor_state(
17009        &r#"AAAAˇone
17010        two
17011
17012        three
17013        fourAAAAˇ
17014        five
17015
17016        siAAAAˇx"#
17017            .unindent(),
17018    );
17019}
17020
17021#[gpui::test]
17022async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17023    init_test(cx, |_| {});
17024
17025    let mut cx = EditorTestContext::new(cx).await;
17026    cx.set_state(
17027        r#"let foo = 1;
17028let foo = 2;
17029let foo = 3;
17030let fooˇ = 4;
17031let foo = 5;
17032let foo = 6;
17033let foo = 7;
17034let foo = 8;
17035let foo = 9;
17036let foo = 10;
17037let foo = 11;
17038let foo = 12;
17039let foo = 13;
17040let foo = 14;
17041let foo = 15;"#,
17042    );
17043
17044    cx.update_editor(|e, window, cx| {
17045        assert_eq!(
17046            e.next_scroll_position,
17047            NextScrollCursorCenterTopBottom::Center,
17048            "Default next scroll direction is center",
17049        );
17050
17051        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17052        assert_eq!(
17053            e.next_scroll_position,
17054            NextScrollCursorCenterTopBottom::Top,
17055            "After center, next scroll direction should be top",
17056        );
17057
17058        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17059        assert_eq!(
17060            e.next_scroll_position,
17061            NextScrollCursorCenterTopBottom::Bottom,
17062            "After top, next scroll direction should be bottom",
17063        );
17064
17065        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17066        assert_eq!(
17067            e.next_scroll_position,
17068            NextScrollCursorCenterTopBottom::Center,
17069            "After bottom, scrolling should start over",
17070        );
17071
17072        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17073        assert_eq!(
17074            e.next_scroll_position,
17075            NextScrollCursorCenterTopBottom::Top,
17076            "Scrolling continues if retriggered fast enough"
17077        );
17078    });
17079
17080    cx.executor()
17081        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17082    cx.executor().run_until_parked();
17083    cx.update_editor(|e, _, _| {
17084        assert_eq!(
17085            e.next_scroll_position,
17086            NextScrollCursorCenterTopBottom::Center,
17087            "If scrolling is not triggered fast enough, it should reset"
17088        );
17089    });
17090}
17091
17092#[gpui::test]
17093async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17094    init_test(cx, |_| {});
17095    let mut cx = EditorLspTestContext::new_rust(
17096        lsp::ServerCapabilities {
17097            definition_provider: Some(lsp::OneOf::Left(true)),
17098            references_provider: Some(lsp::OneOf::Left(true)),
17099            ..lsp::ServerCapabilities::default()
17100        },
17101        cx,
17102    )
17103    .await;
17104
17105    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17106        let go_to_definition = cx
17107            .lsp
17108            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17109                move |params, _| async move {
17110                    if empty_go_to_definition {
17111                        Ok(None)
17112                    } else {
17113                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17114                            uri: params.text_document_position_params.text_document.uri,
17115                            range: lsp::Range::new(
17116                                lsp::Position::new(4, 3),
17117                                lsp::Position::new(4, 6),
17118                            ),
17119                        })))
17120                    }
17121                },
17122            );
17123        let references = cx
17124            .lsp
17125            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17126                Ok(Some(vec![lsp::Location {
17127                    uri: params.text_document_position.text_document.uri,
17128                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17129                }]))
17130            });
17131        (go_to_definition, references)
17132    };
17133
17134    cx.set_state(
17135        &r#"fn one() {
17136            let mut a = ˇtwo();
17137        }
17138
17139        fn two() {}"#
17140            .unindent(),
17141    );
17142    set_up_lsp_handlers(false, &mut cx);
17143    let navigated = cx
17144        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17145        .await
17146        .expect("Failed to navigate to definition");
17147    assert_eq!(
17148        navigated,
17149        Navigated::Yes,
17150        "Should have navigated to definition from the GetDefinition response"
17151    );
17152    cx.assert_editor_state(
17153        &r#"fn one() {
17154            let mut a = two();
17155        }
17156
17157        fn «twoˇ»() {}"#
17158            .unindent(),
17159    );
17160
17161    let editors = cx.update_workspace(|workspace, _, cx| {
17162        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17163    });
17164    cx.update_editor(|_, _, test_editor_cx| {
17165        assert_eq!(
17166            editors.len(),
17167            1,
17168            "Initially, only one, test, editor should be open in the workspace"
17169        );
17170        assert_eq!(
17171            test_editor_cx.entity(),
17172            editors.last().expect("Asserted len is 1").clone()
17173        );
17174    });
17175
17176    set_up_lsp_handlers(true, &mut cx);
17177    let navigated = cx
17178        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17179        .await
17180        .expect("Failed to navigate to lookup references");
17181    assert_eq!(
17182        navigated,
17183        Navigated::Yes,
17184        "Should have navigated to references as a fallback after empty GoToDefinition response"
17185    );
17186    // We should not change the selections in the existing file,
17187    // if opening another milti buffer with the references
17188    cx.assert_editor_state(
17189        &r#"fn one() {
17190            let mut a = two();
17191        }
17192
17193        fn «twoˇ»() {}"#
17194            .unindent(),
17195    );
17196    let editors = cx.update_workspace(|workspace, _, cx| {
17197        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17198    });
17199    cx.update_editor(|_, _, test_editor_cx| {
17200        assert_eq!(
17201            editors.len(),
17202            2,
17203            "After falling back to references search, we open a new editor with the results"
17204        );
17205        let references_fallback_text = editors
17206            .into_iter()
17207            .find(|new_editor| *new_editor != test_editor_cx.entity())
17208            .expect("Should have one non-test editor now")
17209            .read(test_editor_cx)
17210            .text(test_editor_cx);
17211        assert_eq!(
17212            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17213            "Should use the range from the references response and not the GoToDefinition one"
17214        );
17215    });
17216}
17217
17218#[gpui::test]
17219async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17220    init_test(cx, |_| {});
17221    cx.update(|cx| {
17222        let mut editor_settings = EditorSettings::get_global(cx).clone();
17223        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17224        EditorSettings::override_global(editor_settings, cx);
17225    });
17226    let mut cx = EditorLspTestContext::new_rust(
17227        lsp::ServerCapabilities {
17228            definition_provider: Some(lsp::OneOf::Left(true)),
17229            references_provider: Some(lsp::OneOf::Left(true)),
17230            ..lsp::ServerCapabilities::default()
17231        },
17232        cx,
17233    )
17234    .await;
17235    let original_state = r#"fn one() {
17236        let mut a = ˇtwo();
17237    }
17238
17239    fn two() {}"#
17240        .unindent();
17241    cx.set_state(&original_state);
17242
17243    let mut go_to_definition = cx
17244        .lsp
17245        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17246            move |_, _| async move { Ok(None) },
17247        );
17248    let _references = cx
17249        .lsp
17250        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17251            panic!("Should not call for references with no go to definition fallback")
17252        });
17253
17254    let navigated = cx
17255        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17256        .await
17257        .expect("Failed to navigate to lookup references");
17258    go_to_definition
17259        .next()
17260        .await
17261        .expect("Should have called the go_to_definition handler");
17262
17263    assert_eq!(
17264        navigated,
17265        Navigated::No,
17266        "Should have navigated to references as a fallback after empty GoToDefinition response"
17267    );
17268    cx.assert_editor_state(&original_state);
17269    let editors = cx.update_workspace(|workspace, _, cx| {
17270        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17271    });
17272    cx.update_editor(|_, _, _| {
17273        assert_eq!(
17274            editors.len(),
17275            1,
17276            "After unsuccessful fallback, no other editor should have been opened"
17277        );
17278    });
17279}
17280
17281#[gpui::test]
17282async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17283    init_test(cx, |_| {});
17284
17285    let language = Arc::new(Language::new(
17286        LanguageConfig::default(),
17287        Some(tree_sitter_rust::LANGUAGE.into()),
17288    ));
17289
17290    let text = r#"
17291        #[cfg(test)]
17292        mod tests() {
17293            #[test]
17294            fn runnable_1() {
17295                let a = 1;
17296            }
17297
17298            #[test]
17299            fn runnable_2() {
17300                let a = 1;
17301                let b = 2;
17302            }
17303        }
17304    "#
17305    .unindent();
17306
17307    let fs = FakeFs::new(cx.executor());
17308    fs.insert_file("/file.rs", Default::default()).await;
17309
17310    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17311    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17312    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17313    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17314    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17315
17316    let editor = cx.new_window_entity(|window, cx| {
17317        Editor::new(
17318            EditorMode::full(),
17319            multi_buffer,
17320            Some(project.clone()),
17321            window,
17322            cx,
17323        )
17324    });
17325
17326    editor.update_in(cx, |editor, window, cx| {
17327        let snapshot = editor.buffer().read(cx).snapshot(cx);
17328        editor.tasks.insert(
17329            (buffer.read(cx).remote_id(), 3),
17330            RunnableTasks {
17331                templates: vec![],
17332                offset: snapshot.anchor_before(43),
17333                column: 0,
17334                extra_variables: HashMap::default(),
17335                context_range: BufferOffset(43)..BufferOffset(85),
17336            },
17337        );
17338        editor.tasks.insert(
17339            (buffer.read(cx).remote_id(), 8),
17340            RunnableTasks {
17341                templates: vec![],
17342                offset: snapshot.anchor_before(86),
17343                column: 0,
17344                extra_variables: HashMap::default(),
17345                context_range: BufferOffset(86)..BufferOffset(191),
17346            },
17347        );
17348
17349        // Test finding task when cursor is inside function body
17350        editor.change_selections(None, window, cx, |s| {
17351            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17352        });
17353        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17354        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17355
17356        // Test finding task when cursor is on function name
17357        editor.change_selections(None, window, cx, |s| {
17358            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17359        });
17360        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17361        assert_eq!(row, 8, "Should find task when cursor is on function name");
17362    });
17363}
17364
17365#[gpui::test]
17366async fn test_folding_buffers(cx: &mut TestAppContext) {
17367    init_test(cx, |_| {});
17368
17369    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17370    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17371    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17372
17373    let fs = FakeFs::new(cx.executor());
17374    fs.insert_tree(
17375        path!("/a"),
17376        json!({
17377            "first.rs": sample_text_1,
17378            "second.rs": sample_text_2,
17379            "third.rs": sample_text_3,
17380        }),
17381    )
17382    .await;
17383    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17384    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17385    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17386    let worktree = project.update(cx, |project, cx| {
17387        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17388        assert_eq!(worktrees.len(), 1);
17389        worktrees.pop().unwrap()
17390    });
17391    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17392
17393    let buffer_1 = project
17394        .update(cx, |project, cx| {
17395            project.open_buffer((worktree_id, "first.rs"), cx)
17396        })
17397        .await
17398        .unwrap();
17399    let buffer_2 = project
17400        .update(cx, |project, cx| {
17401            project.open_buffer((worktree_id, "second.rs"), cx)
17402        })
17403        .await
17404        .unwrap();
17405    let buffer_3 = project
17406        .update(cx, |project, cx| {
17407            project.open_buffer((worktree_id, "third.rs"), cx)
17408        })
17409        .await
17410        .unwrap();
17411
17412    let multi_buffer = cx.new(|cx| {
17413        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17414        multi_buffer.push_excerpts(
17415            buffer_1.clone(),
17416            [
17417                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17418                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17419                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17420            ],
17421            cx,
17422        );
17423        multi_buffer.push_excerpts(
17424            buffer_2.clone(),
17425            [
17426                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17427                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17428                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17429            ],
17430            cx,
17431        );
17432        multi_buffer.push_excerpts(
17433            buffer_3.clone(),
17434            [
17435                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17436                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17437                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17438            ],
17439            cx,
17440        );
17441        multi_buffer
17442    });
17443    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17444        Editor::new(
17445            EditorMode::full(),
17446            multi_buffer.clone(),
17447            Some(project.clone()),
17448            window,
17449            cx,
17450        )
17451    });
17452
17453    assert_eq!(
17454        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17455        "\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",
17456    );
17457
17458    multi_buffer_editor.update(cx, |editor, cx| {
17459        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17460    });
17461    assert_eq!(
17462        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17463        "\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",
17464        "After folding the first buffer, its text should not be displayed"
17465    );
17466
17467    multi_buffer_editor.update(cx, |editor, cx| {
17468        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17469    });
17470    assert_eq!(
17471        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17472        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17473        "After folding the second buffer, its text should not be displayed"
17474    );
17475
17476    multi_buffer_editor.update(cx, |editor, cx| {
17477        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17478    });
17479    assert_eq!(
17480        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17481        "\n\n\n\n\n",
17482        "After folding the third buffer, its text should not be displayed"
17483    );
17484
17485    // Emulate selection inside the fold logic, that should work
17486    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17487        editor
17488            .snapshot(window, cx)
17489            .next_line_boundary(Point::new(0, 4));
17490    });
17491
17492    multi_buffer_editor.update(cx, |editor, cx| {
17493        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17494    });
17495    assert_eq!(
17496        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17497        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17498        "After unfolding the second buffer, its text should be displayed"
17499    );
17500
17501    // Typing inside of buffer 1 causes that buffer to be unfolded.
17502    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17503        assert_eq!(
17504            multi_buffer
17505                .read(cx)
17506                .snapshot(cx)
17507                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17508                .collect::<String>(),
17509            "bbbb"
17510        );
17511        editor.change_selections(None, window, cx, |selections| {
17512            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17513        });
17514        editor.handle_input("B", window, cx);
17515    });
17516
17517    assert_eq!(
17518        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17519        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17520        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17521    );
17522
17523    multi_buffer_editor.update(cx, |editor, cx| {
17524        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17525    });
17526    assert_eq!(
17527        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17528        "\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",
17529        "After unfolding the all buffers, all original text should be displayed"
17530    );
17531}
17532
17533#[gpui::test]
17534async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17535    init_test(cx, |_| {});
17536
17537    let sample_text_1 = "1111\n2222\n3333".to_string();
17538    let sample_text_2 = "4444\n5555\n6666".to_string();
17539    let sample_text_3 = "7777\n8888\n9999".to_string();
17540
17541    let fs = FakeFs::new(cx.executor());
17542    fs.insert_tree(
17543        path!("/a"),
17544        json!({
17545            "first.rs": sample_text_1,
17546            "second.rs": sample_text_2,
17547            "third.rs": sample_text_3,
17548        }),
17549    )
17550    .await;
17551    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17552    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17553    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17554    let worktree = project.update(cx, |project, cx| {
17555        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17556        assert_eq!(worktrees.len(), 1);
17557        worktrees.pop().unwrap()
17558    });
17559    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17560
17561    let buffer_1 = project
17562        .update(cx, |project, cx| {
17563            project.open_buffer((worktree_id, "first.rs"), cx)
17564        })
17565        .await
17566        .unwrap();
17567    let buffer_2 = project
17568        .update(cx, |project, cx| {
17569            project.open_buffer((worktree_id, "second.rs"), cx)
17570        })
17571        .await
17572        .unwrap();
17573    let buffer_3 = project
17574        .update(cx, |project, cx| {
17575            project.open_buffer((worktree_id, "third.rs"), cx)
17576        })
17577        .await
17578        .unwrap();
17579
17580    let multi_buffer = cx.new(|cx| {
17581        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17582        multi_buffer.push_excerpts(
17583            buffer_1.clone(),
17584            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17585            cx,
17586        );
17587        multi_buffer.push_excerpts(
17588            buffer_2.clone(),
17589            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17590            cx,
17591        );
17592        multi_buffer.push_excerpts(
17593            buffer_3.clone(),
17594            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17595            cx,
17596        );
17597        multi_buffer
17598    });
17599
17600    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17601        Editor::new(
17602            EditorMode::full(),
17603            multi_buffer,
17604            Some(project.clone()),
17605            window,
17606            cx,
17607        )
17608    });
17609
17610    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17611    assert_eq!(
17612        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17613        full_text,
17614    );
17615
17616    multi_buffer_editor.update(cx, |editor, cx| {
17617        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17618    });
17619    assert_eq!(
17620        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17621        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17622        "After folding the first buffer, its text should not be displayed"
17623    );
17624
17625    multi_buffer_editor.update(cx, |editor, cx| {
17626        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17627    });
17628
17629    assert_eq!(
17630        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17631        "\n\n\n\n\n\n7777\n8888\n9999",
17632        "After folding the second buffer, its text should not be displayed"
17633    );
17634
17635    multi_buffer_editor.update(cx, |editor, cx| {
17636        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17637    });
17638    assert_eq!(
17639        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17640        "\n\n\n\n\n",
17641        "After folding the third buffer, its text should not be displayed"
17642    );
17643
17644    multi_buffer_editor.update(cx, |editor, cx| {
17645        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17646    });
17647    assert_eq!(
17648        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17649        "\n\n\n\n4444\n5555\n6666\n\n",
17650        "After unfolding the second buffer, its text should be displayed"
17651    );
17652
17653    multi_buffer_editor.update(cx, |editor, cx| {
17654        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17655    });
17656    assert_eq!(
17657        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17658        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17659        "After unfolding the first buffer, its text should be displayed"
17660    );
17661
17662    multi_buffer_editor.update(cx, |editor, cx| {
17663        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17664    });
17665    assert_eq!(
17666        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17667        full_text,
17668        "After unfolding all buffers, all original text should be displayed"
17669    );
17670}
17671
17672#[gpui::test]
17673async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17674    init_test(cx, |_| {});
17675
17676    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17677
17678    let fs = FakeFs::new(cx.executor());
17679    fs.insert_tree(
17680        path!("/a"),
17681        json!({
17682            "main.rs": sample_text,
17683        }),
17684    )
17685    .await;
17686    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17687    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17688    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17689    let worktree = project.update(cx, |project, cx| {
17690        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17691        assert_eq!(worktrees.len(), 1);
17692        worktrees.pop().unwrap()
17693    });
17694    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17695
17696    let buffer_1 = project
17697        .update(cx, |project, cx| {
17698            project.open_buffer((worktree_id, "main.rs"), cx)
17699        })
17700        .await
17701        .unwrap();
17702
17703    let multi_buffer = cx.new(|cx| {
17704        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17705        multi_buffer.push_excerpts(
17706            buffer_1.clone(),
17707            [ExcerptRange::new(
17708                Point::new(0, 0)
17709                    ..Point::new(
17710                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17711                        0,
17712                    ),
17713            )],
17714            cx,
17715        );
17716        multi_buffer
17717    });
17718    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17719        Editor::new(
17720            EditorMode::full(),
17721            multi_buffer,
17722            Some(project.clone()),
17723            window,
17724            cx,
17725        )
17726    });
17727
17728    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17729    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17730        enum TestHighlight {}
17731        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17732        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17733        editor.highlight_text::<TestHighlight>(
17734            vec![highlight_range.clone()],
17735            HighlightStyle::color(Hsla::green()),
17736            cx,
17737        );
17738        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17739    });
17740
17741    let full_text = format!("\n\n{sample_text}");
17742    assert_eq!(
17743        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17744        full_text,
17745    );
17746}
17747
17748#[gpui::test]
17749async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17750    init_test(cx, |_| {});
17751    cx.update(|cx| {
17752        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17753            "keymaps/default-linux.json",
17754            cx,
17755        )
17756        .unwrap();
17757        cx.bind_keys(default_key_bindings);
17758    });
17759
17760    let (editor, cx) = cx.add_window_view(|window, cx| {
17761        let multi_buffer = MultiBuffer::build_multi(
17762            [
17763                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17764                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17765                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17766                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17767            ],
17768            cx,
17769        );
17770        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17771
17772        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17773        // fold all but the second buffer, so that we test navigating between two
17774        // adjacent folded buffers, as well as folded buffers at the start and
17775        // end the multibuffer
17776        editor.fold_buffer(buffer_ids[0], cx);
17777        editor.fold_buffer(buffer_ids[2], cx);
17778        editor.fold_buffer(buffer_ids[3], cx);
17779
17780        editor
17781    });
17782    cx.simulate_resize(size(px(1000.), px(1000.)));
17783
17784    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
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("down");
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("down");
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("down");
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    cx.simulate_keystroke("down");
17837    cx.assert_excerpts_with_selections(indoc! {"
17838        [EXCERPT]
17839        [FOLDED]
17840        [EXCERPT]
17841        a1
17842        b1
17843        [EXCERPT]
17844        ˇ[FOLDED]
17845        [EXCERPT]
17846        [FOLDED]
17847        "
17848    });
17849    for _ in 0..5 {
17850        cx.simulate_keystroke("down");
17851        cx.assert_excerpts_with_selections(indoc! {"
17852            [EXCERPT]
17853            [FOLDED]
17854            [EXCERPT]
17855            a1
17856            b1
17857            [EXCERPT]
17858            [FOLDED]
17859            [EXCERPT]
17860            ˇ[FOLDED]
17861            "
17862        });
17863    }
17864
17865    cx.simulate_keystroke("up");
17866    cx.assert_excerpts_with_selections(indoc! {"
17867        [EXCERPT]
17868        [FOLDED]
17869        [EXCERPT]
17870        a1
17871        b1
17872        [EXCERPT]
17873        ˇ[FOLDED]
17874        [EXCERPT]
17875        [FOLDED]
17876        "
17877    });
17878    cx.simulate_keystroke("up");
17879    cx.assert_excerpts_with_selections(indoc! {"
17880        [EXCERPT]
17881        [FOLDED]
17882        [EXCERPT]
17883        a1
17884        b1
17885        ˇ[EXCERPT]
17886        [FOLDED]
17887        [EXCERPT]
17888        [FOLDED]
17889        "
17890    });
17891    cx.simulate_keystroke("up");
17892    cx.assert_excerpts_with_selections(indoc! {"
17893        [EXCERPT]
17894        [FOLDED]
17895        [EXCERPT]
17896        a1
17897        ˇb1
17898        [EXCERPT]
17899        [FOLDED]
17900        [EXCERPT]
17901        [FOLDED]
17902        "
17903    });
17904    cx.simulate_keystroke("up");
17905    cx.assert_excerpts_with_selections(indoc! {"
17906        [EXCERPT]
17907        [FOLDED]
17908        [EXCERPT]
17909        ˇa1
17910        b1
17911        [EXCERPT]
17912        [FOLDED]
17913        [EXCERPT]
17914        [FOLDED]
17915        "
17916    });
17917    for _ in 0..5 {
17918        cx.simulate_keystroke("up");
17919        cx.assert_excerpts_with_selections(indoc! {"
17920            [EXCERPT]
17921            ˇ[FOLDED]
17922            [EXCERPT]
17923            a1
17924            b1
17925            [EXCERPT]
17926            [FOLDED]
17927            [EXCERPT]
17928            [FOLDED]
17929            "
17930        });
17931    }
17932}
17933
17934#[gpui::test]
17935async fn test_inline_completion_text(cx: &mut TestAppContext) {
17936    init_test(cx, |_| {});
17937
17938    // Simple insertion
17939    assert_highlighted_edits(
17940        "Hello, world!",
17941        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17942        true,
17943        cx,
17944        |highlighted_edits, cx| {
17945            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17946            assert_eq!(highlighted_edits.highlights.len(), 1);
17947            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17948            assert_eq!(
17949                highlighted_edits.highlights[0].1.background_color,
17950                Some(cx.theme().status().created_background)
17951            );
17952        },
17953    )
17954    .await;
17955
17956    // Replacement
17957    assert_highlighted_edits(
17958        "This is a test.",
17959        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17960        false,
17961        cx,
17962        |highlighted_edits, cx| {
17963            assert_eq!(highlighted_edits.text, "That is a test.");
17964            assert_eq!(highlighted_edits.highlights.len(), 1);
17965            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17966            assert_eq!(
17967                highlighted_edits.highlights[0].1.background_color,
17968                Some(cx.theme().status().created_background)
17969            );
17970        },
17971    )
17972    .await;
17973
17974    // Multiple edits
17975    assert_highlighted_edits(
17976        "Hello, world!",
17977        vec![
17978            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17979            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17980        ],
17981        false,
17982        cx,
17983        |highlighted_edits, cx| {
17984            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17985            assert_eq!(highlighted_edits.highlights.len(), 2);
17986            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17987            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17988            assert_eq!(
17989                highlighted_edits.highlights[0].1.background_color,
17990                Some(cx.theme().status().created_background)
17991            );
17992            assert_eq!(
17993                highlighted_edits.highlights[1].1.background_color,
17994                Some(cx.theme().status().created_background)
17995            );
17996        },
17997    )
17998    .await;
17999
18000    // Multiple lines with edits
18001    assert_highlighted_edits(
18002        "First line\nSecond line\nThird line\nFourth line",
18003        vec![
18004            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18005            (
18006                Point::new(2, 0)..Point::new(2, 10),
18007                "New third line".to_string(),
18008            ),
18009            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18010        ],
18011        false,
18012        cx,
18013        |highlighted_edits, cx| {
18014            assert_eq!(
18015                highlighted_edits.text,
18016                "Second modified\nNew third line\nFourth updated line"
18017            );
18018            assert_eq!(highlighted_edits.highlights.len(), 3);
18019            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18020            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18021            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18022            for highlight in &highlighted_edits.highlights {
18023                assert_eq!(
18024                    highlight.1.background_color,
18025                    Some(cx.theme().status().created_background)
18026                );
18027            }
18028        },
18029    )
18030    .await;
18031}
18032
18033#[gpui::test]
18034async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18035    init_test(cx, |_| {});
18036
18037    // Deletion
18038    assert_highlighted_edits(
18039        "Hello, world!",
18040        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18041        true,
18042        cx,
18043        |highlighted_edits, cx| {
18044            assert_eq!(highlighted_edits.text, "Hello, world!");
18045            assert_eq!(highlighted_edits.highlights.len(), 1);
18046            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18047            assert_eq!(
18048                highlighted_edits.highlights[0].1.background_color,
18049                Some(cx.theme().status().deleted_background)
18050            );
18051        },
18052    )
18053    .await;
18054
18055    // Insertion
18056    assert_highlighted_edits(
18057        "Hello, world!",
18058        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18059        true,
18060        cx,
18061        |highlighted_edits, cx| {
18062            assert_eq!(highlighted_edits.highlights.len(), 1);
18063            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18064            assert_eq!(
18065                highlighted_edits.highlights[0].1.background_color,
18066                Some(cx.theme().status().created_background)
18067            );
18068        },
18069    )
18070    .await;
18071}
18072
18073async fn assert_highlighted_edits(
18074    text: &str,
18075    edits: Vec<(Range<Point>, String)>,
18076    include_deletions: bool,
18077    cx: &mut TestAppContext,
18078    assertion_fn: impl Fn(HighlightedText, &App),
18079) {
18080    let window = cx.add_window(|window, cx| {
18081        let buffer = MultiBuffer::build_simple(text, cx);
18082        Editor::new(EditorMode::full(), buffer, None, window, cx)
18083    });
18084    let cx = &mut VisualTestContext::from_window(*window, cx);
18085
18086    let (buffer, snapshot) = window
18087        .update(cx, |editor, _window, cx| {
18088            (
18089                editor.buffer().clone(),
18090                editor.buffer().read(cx).snapshot(cx),
18091            )
18092        })
18093        .unwrap();
18094
18095    let edits = edits
18096        .into_iter()
18097        .map(|(range, edit)| {
18098            (
18099                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18100                edit,
18101            )
18102        })
18103        .collect::<Vec<_>>();
18104
18105    let text_anchor_edits = edits
18106        .clone()
18107        .into_iter()
18108        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18109        .collect::<Vec<_>>();
18110
18111    let edit_preview = window
18112        .update(cx, |_, _window, cx| {
18113            buffer
18114                .read(cx)
18115                .as_singleton()
18116                .unwrap()
18117                .read(cx)
18118                .preview_edits(text_anchor_edits.into(), cx)
18119        })
18120        .unwrap()
18121        .await;
18122
18123    cx.update(|_window, cx| {
18124        let highlighted_edits = inline_completion_edit_text(
18125            &snapshot.as_singleton().unwrap().2,
18126            &edits,
18127            &edit_preview,
18128            include_deletions,
18129            cx,
18130        );
18131        assertion_fn(highlighted_edits, cx)
18132    });
18133}
18134
18135#[track_caller]
18136fn assert_breakpoint(
18137    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18138    path: &Arc<Path>,
18139    expected: Vec<(u32, Breakpoint)>,
18140) {
18141    if expected.len() == 0usize {
18142        assert!(!breakpoints.contains_key(path), "{}", path.display());
18143    } else {
18144        let mut breakpoint = breakpoints
18145            .get(path)
18146            .unwrap()
18147            .into_iter()
18148            .map(|breakpoint| {
18149                (
18150                    breakpoint.row,
18151                    Breakpoint {
18152                        message: breakpoint.message.clone(),
18153                        state: breakpoint.state,
18154                        condition: breakpoint.condition.clone(),
18155                        hit_condition: breakpoint.hit_condition.clone(),
18156                    },
18157                )
18158            })
18159            .collect::<Vec<_>>();
18160
18161        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18162
18163        assert_eq!(expected, breakpoint);
18164    }
18165}
18166
18167fn add_log_breakpoint_at_cursor(
18168    editor: &mut Editor,
18169    log_message: &str,
18170    window: &mut Window,
18171    cx: &mut Context<Editor>,
18172) {
18173    let (anchor, bp) = editor
18174        .breakpoints_at_cursors(window, cx)
18175        .first()
18176        .and_then(|(anchor, bp)| {
18177            if let Some(bp) = bp {
18178                Some((*anchor, bp.clone()))
18179            } else {
18180                None
18181            }
18182        })
18183        .unwrap_or_else(|| {
18184            let cursor_position: Point = editor.selections.newest(cx).head();
18185
18186            let breakpoint_position = editor
18187                .snapshot(window, cx)
18188                .display_snapshot
18189                .buffer_snapshot
18190                .anchor_before(Point::new(cursor_position.row, 0));
18191
18192            (breakpoint_position, Breakpoint::new_log(&log_message))
18193        });
18194
18195    editor.edit_breakpoint_at_anchor(
18196        anchor,
18197        bp,
18198        BreakpointEditAction::EditLogMessage(log_message.into()),
18199        cx,
18200    );
18201}
18202
18203#[gpui::test]
18204async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18205    init_test(cx, |_| {});
18206
18207    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18208    let fs = FakeFs::new(cx.executor());
18209    fs.insert_tree(
18210        path!("/a"),
18211        json!({
18212            "main.rs": sample_text,
18213        }),
18214    )
18215    .await;
18216    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18217    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18218    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18219
18220    let fs = FakeFs::new(cx.executor());
18221    fs.insert_tree(
18222        path!("/a"),
18223        json!({
18224            "main.rs": sample_text,
18225        }),
18226    )
18227    .await;
18228    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18229    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18230    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18231    let worktree_id = workspace
18232        .update(cx, |workspace, _window, cx| {
18233            workspace.project().update(cx, |project, cx| {
18234                project.worktrees(cx).next().unwrap().read(cx).id()
18235            })
18236        })
18237        .unwrap();
18238
18239    let buffer = project
18240        .update(cx, |project, cx| {
18241            project.open_buffer((worktree_id, "main.rs"), cx)
18242        })
18243        .await
18244        .unwrap();
18245
18246    let (editor, cx) = cx.add_window_view(|window, cx| {
18247        Editor::new(
18248            EditorMode::full(),
18249            MultiBuffer::build_from_buffer(buffer, cx),
18250            Some(project.clone()),
18251            window,
18252            cx,
18253        )
18254    });
18255
18256    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18257    let abs_path = project.read_with(cx, |project, cx| {
18258        project
18259            .absolute_path(&project_path, cx)
18260            .map(|path_buf| Arc::from(path_buf.to_owned()))
18261            .unwrap()
18262    });
18263
18264    // assert we can add breakpoint on the first line
18265    editor.update_in(cx, |editor, window, cx| {
18266        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18267        editor.move_to_end(&MoveToEnd, window, cx);
18268        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18269    });
18270
18271    let breakpoints = editor.update(cx, |editor, cx| {
18272        editor
18273            .breakpoint_store()
18274            .as_ref()
18275            .unwrap()
18276            .read(cx)
18277            .all_breakpoints(cx)
18278            .clone()
18279    });
18280
18281    assert_eq!(1, breakpoints.len());
18282    assert_breakpoint(
18283        &breakpoints,
18284        &abs_path,
18285        vec![
18286            (0, Breakpoint::new_standard()),
18287            (3, Breakpoint::new_standard()),
18288        ],
18289    );
18290
18291    editor.update_in(cx, |editor, window, cx| {
18292        editor.move_to_beginning(&MoveToBeginning, window, cx);
18293        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18294    });
18295
18296    let breakpoints = editor.update(cx, |editor, cx| {
18297        editor
18298            .breakpoint_store()
18299            .as_ref()
18300            .unwrap()
18301            .read(cx)
18302            .all_breakpoints(cx)
18303            .clone()
18304    });
18305
18306    assert_eq!(1, breakpoints.len());
18307    assert_breakpoint(
18308        &breakpoints,
18309        &abs_path,
18310        vec![(3, Breakpoint::new_standard())],
18311    );
18312
18313    editor.update_in(cx, |editor, window, cx| {
18314        editor.move_to_end(&MoveToEnd, window, cx);
18315        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18316    });
18317
18318    let breakpoints = editor.update(cx, |editor, cx| {
18319        editor
18320            .breakpoint_store()
18321            .as_ref()
18322            .unwrap()
18323            .read(cx)
18324            .all_breakpoints(cx)
18325            .clone()
18326    });
18327
18328    assert_eq!(0, breakpoints.len());
18329    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18330}
18331
18332#[gpui::test]
18333async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18334    init_test(cx, |_| {});
18335
18336    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18337
18338    let fs = FakeFs::new(cx.executor());
18339    fs.insert_tree(
18340        path!("/a"),
18341        json!({
18342            "main.rs": sample_text,
18343        }),
18344    )
18345    .await;
18346    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18347    let (workspace, cx) =
18348        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18349
18350    let worktree_id = workspace.update(cx, |workspace, cx| {
18351        workspace.project().update(cx, |project, cx| {
18352            project.worktrees(cx).next().unwrap().read(cx).id()
18353        })
18354    });
18355
18356    let buffer = project
18357        .update(cx, |project, cx| {
18358            project.open_buffer((worktree_id, "main.rs"), cx)
18359        })
18360        .await
18361        .unwrap();
18362
18363    let (editor, cx) = cx.add_window_view(|window, cx| {
18364        Editor::new(
18365            EditorMode::full(),
18366            MultiBuffer::build_from_buffer(buffer, cx),
18367            Some(project.clone()),
18368            window,
18369            cx,
18370        )
18371    });
18372
18373    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18374    let abs_path = project.read_with(cx, |project, cx| {
18375        project
18376            .absolute_path(&project_path, cx)
18377            .map(|path_buf| Arc::from(path_buf.to_owned()))
18378            .unwrap()
18379    });
18380
18381    editor.update_in(cx, |editor, window, cx| {
18382        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18383    });
18384
18385    let breakpoints = editor.update(cx, |editor, cx| {
18386        editor
18387            .breakpoint_store()
18388            .as_ref()
18389            .unwrap()
18390            .read(cx)
18391            .all_breakpoints(cx)
18392            .clone()
18393    });
18394
18395    assert_breakpoint(
18396        &breakpoints,
18397        &abs_path,
18398        vec![(0, Breakpoint::new_log("hello world"))],
18399    );
18400
18401    // Removing a log message from a log breakpoint should remove it
18402    editor.update_in(cx, |editor, window, cx| {
18403        add_log_breakpoint_at_cursor(editor, "", window, cx);
18404    });
18405
18406    let breakpoints = editor.update(cx, |editor, cx| {
18407        editor
18408            .breakpoint_store()
18409            .as_ref()
18410            .unwrap()
18411            .read(cx)
18412            .all_breakpoints(cx)
18413            .clone()
18414    });
18415
18416    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18417
18418    editor.update_in(cx, |editor, window, cx| {
18419        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18420        editor.move_to_end(&MoveToEnd, window, cx);
18421        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18422        // Not adding a log message to a standard breakpoint shouldn't remove it
18423        add_log_breakpoint_at_cursor(editor, "", window, cx);
18424    });
18425
18426    let breakpoints = editor.update(cx, |editor, cx| {
18427        editor
18428            .breakpoint_store()
18429            .as_ref()
18430            .unwrap()
18431            .read(cx)
18432            .all_breakpoints(cx)
18433            .clone()
18434    });
18435
18436    assert_breakpoint(
18437        &breakpoints,
18438        &abs_path,
18439        vec![
18440            (0, Breakpoint::new_standard()),
18441            (3, Breakpoint::new_standard()),
18442        ],
18443    );
18444
18445    editor.update_in(cx, |editor, window, cx| {
18446        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18447    });
18448
18449    let breakpoints = editor.update(cx, |editor, cx| {
18450        editor
18451            .breakpoint_store()
18452            .as_ref()
18453            .unwrap()
18454            .read(cx)
18455            .all_breakpoints(cx)
18456            .clone()
18457    });
18458
18459    assert_breakpoint(
18460        &breakpoints,
18461        &abs_path,
18462        vec![
18463            (0, Breakpoint::new_standard()),
18464            (3, Breakpoint::new_log("hello world")),
18465        ],
18466    );
18467
18468    editor.update_in(cx, |editor, window, cx| {
18469        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18470    });
18471
18472    let breakpoints = editor.update(cx, |editor, cx| {
18473        editor
18474            .breakpoint_store()
18475            .as_ref()
18476            .unwrap()
18477            .read(cx)
18478            .all_breakpoints(cx)
18479            .clone()
18480    });
18481
18482    assert_breakpoint(
18483        &breakpoints,
18484        &abs_path,
18485        vec![
18486            (0, Breakpoint::new_standard()),
18487            (3, Breakpoint::new_log("hello Earth!!")),
18488        ],
18489    );
18490}
18491
18492/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18493/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18494/// or when breakpoints were placed out of order. This tests for a regression too
18495#[gpui::test]
18496async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18497    init_test(cx, |_| {});
18498
18499    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18500    let fs = FakeFs::new(cx.executor());
18501    fs.insert_tree(
18502        path!("/a"),
18503        json!({
18504            "main.rs": sample_text,
18505        }),
18506    )
18507    .await;
18508    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18509    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18510    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18511
18512    let fs = FakeFs::new(cx.executor());
18513    fs.insert_tree(
18514        path!("/a"),
18515        json!({
18516            "main.rs": sample_text,
18517        }),
18518    )
18519    .await;
18520    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18521    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18522    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18523    let worktree_id = workspace
18524        .update(cx, |workspace, _window, cx| {
18525            workspace.project().update(cx, |project, cx| {
18526                project.worktrees(cx).next().unwrap().read(cx).id()
18527            })
18528        })
18529        .unwrap();
18530
18531    let buffer = project
18532        .update(cx, |project, cx| {
18533            project.open_buffer((worktree_id, "main.rs"), cx)
18534        })
18535        .await
18536        .unwrap();
18537
18538    let (editor, cx) = cx.add_window_view(|window, cx| {
18539        Editor::new(
18540            EditorMode::full(),
18541            MultiBuffer::build_from_buffer(buffer, cx),
18542            Some(project.clone()),
18543            window,
18544            cx,
18545        )
18546    });
18547
18548    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18549    let abs_path = project.read_with(cx, |project, cx| {
18550        project
18551            .absolute_path(&project_path, cx)
18552            .map(|path_buf| Arc::from(path_buf.to_owned()))
18553            .unwrap()
18554    });
18555
18556    // assert we can add breakpoint on the first line
18557    editor.update_in(cx, |editor, window, cx| {
18558        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18559        editor.move_to_end(&MoveToEnd, window, cx);
18560        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18561        editor.move_up(&MoveUp, window, cx);
18562        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18563    });
18564
18565    let breakpoints = editor.update(cx, |editor, cx| {
18566        editor
18567            .breakpoint_store()
18568            .as_ref()
18569            .unwrap()
18570            .read(cx)
18571            .all_breakpoints(cx)
18572            .clone()
18573    });
18574
18575    assert_eq!(1, breakpoints.len());
18576    assert_breakpoint(
18577        &breakpoints,
18578        &abs_path,
18579        vec![
18580            (0, Breakpoint::new_standard()),
18581            (2, Breakpoint::new_standard()),
18582            (3, Breakpoint::new_standard()),
18583        ],
18584    );
18585
18586    editor.update_in(cx, |editor, window, cx| {
18587        editor.move_to_beginning(&MoveToBeginning, window, cx);
18588        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18589        editor.move_to_end(&MoveToEnd, window, cx);
18590        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18591        // Disabling a breakpoint that doesn't exist should do nothing
18592        editor.move_up(&MoveUp, window, cx);
18593        editor.move_up(&MoveUp, window, cx);
18594        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18595    });
18596
18597    let breakpoints = editor.update(cx, |editor, cx| {
18598        editor
18599            .breakpoint_store()
18600            .as_ref()
18601            .unwrap()
18602            .read(cx)
18603            .all_breakpoints(cx)
18604            .clone()
18605    });
18606
18607    let disable_breakpoint = {
18608        let mut bp = Breakpoint::new_standard();
18609        bp.state = BreakpointState::Disabled;
18610        bp
18611    };
18612
18613    assert_eq!(1, breakpoints.len());
18614    assert_breakpoint(
18615        &breakpoints,
18616        &abs_path,
18617        vec![
18618            (0, disable_breakpoint.clone()),
18619            (2, Breakpoint::new_standard()),
18620            (3, disable_breakpoint.clone()),
18621        ],
18622    );
18623
18624    editor.update_in(cx, |editor, window, cx| {
18625        editor.move_to_beginning(&MoveToBeginning, window, cx);
18626        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18627        editor.move_to_end(&MoveToEnd, window, cx);
18628        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18629        editor.move_up(&MoveUp, window, cx);
18630        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18631    });
18632
18633    let breakpoints = editor.update(cx, |editor, cx| {
18634        editor
18635            .breakpoint_store()
18636            .as_ref()
18637            .unwrap()
18638            .read(cx)
18639            .all_breakpoints(cx)
18640            .clone()
18641    });
18642
18643    assert_eq!(1, breakpoints.len());
18644    assert_breakpoint(
18645        &breakpoints,
18646        &abs_path,
18647        vec![
18648            (0, Breakpoint::new_standard()),
18649            (2, disable_breakpoint),
18650            (3, Breakpoint::new_standard()),
18651        ],
18652    );
18653}
18654
18655#[gpui::test]
18656async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18657    init_test(cx, |_| {});
18658    let capabilities = lsp::ServerCapabilities {
18659        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18660            prepare_provider: Some(true),
18661            work_done_progress_options: Default::default(),
18662        })),
18663        ..Default::default()
18664    };
18665    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18666
18667    cx.set_state(indoc! {"
18668        struct Fˇoo {}
18669    "});
18670
18671    cx.update_editor(|editor, _, cx| {
18672        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18673        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18674        editor.highlight_background::<DocumentHighlightRead>(
18675            &[highlight_range],
18676            |c| c.editor_document_highlight_read_background,
18677            cx,
18678        );
18679    });
18680
18681    let mut prepare_rename_handler = cx
18682        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18683            move |_, _, _| async move {
18684                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18685                    start: lsp::Position {
18686                        line: 0,
18687                        character: 7,
18688                    },
18689                    end: lsp::Position {
18690                        line: 0,
18691                        character: 10,
18692                    },
18693                })))
18694            },
18695        );
18696    let prepare_rename_task = cx
18697        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18698        .expect("Prepare rename was not started");
18699    prepare_rename_handler.next().await.unwrap();
18700    prepare_rename_task.await.expect("Prepare rename failed");
18701
18702    let mut rename_handler =
18703        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18704            let edit = lsp::TextEdit {
18705                range: lsp::Range {
18706                    start: lsp::Position {
18707                        line: 0,
18708                        character: 7,
18709                    },
18710                    end: lsp::Position {
18711                        line: 0,
18712                        character: 10,
18713                    },
18714                },
18715                new_text: "FooRenamed".to_string(),
18716            };
18717            Ok(Some(lsp::WorkspaceEdit::new(
18718                // Specify the same edit twice
18719                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18720            )))
18721        });
18722    let rename_task = cx
18723        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18724        .expect("Confirm rename was not started");
18725    rename_handler.next().await.unwrap();
18726    rename_task.await.expect("Confirm rename failed");
18727    cx.run_until_parked();
18728
18729    // Despite two edits, only one is actually applied as those are identical
18730    cx.assert_editor_state(indoc! {"
18731        struct FooRenamedˇ {}
18732    "});
18733}
18734
18735#[gpui::test]
18736async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18737    init_test(cx, |_| {});
18738    // These capabilities indicate that the server does not support prepare rename.
18739    let capabilities = lsp::ServerCapabilities {
18740        rename_provider: Some(lsp::OneOf::Left(true)),
18741        ..Default::default()
18742    };
18743    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18744
18745    cx.set_state(indoc! {"
18746        struct Fˇoo {}
18747    "});
18748
18749    cx.update_editor(|editor, _window, cx| {
18750        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18751        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18752        editor.highlight_background::<DocumentHighlightRead>(
18753            &[highlight_range],
18754            |c| c.editor_document_highlight_read_background,
18755            cx,
18756        );
18757    });
18758
18759    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18760        .expect("Prepare rename was not started")
18761        .await
18762        .expect("Prepare rename failed");
18763
18764    let mut rename_handler =
18765        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18766            let edit = lsp::TextEdit {
18767                range: lsp::Range {
18768                    start: lsp::Position {
18769                        line: 0,
18770                        character: 7,
18771                    },
18772                    end: lsp::Position {
18773                        line: 0,
18774                        character: 10,
18775                    },
18776                },
18777                new_text: "FooRenamed".to_string(),
18778            };
18779            Ok(Some(lsp::WorkspaceEdit::new(
18780                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18781            )))
18782        });
18783    let rename_task = cx
18784        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18785        .expect("Confirm rename was not started");
18786    rename_handler.next().await.unwrap();
18787    rename_task.await.expect("Confirm rename failed");
18788    cx.run_until_parked();
18789
18790    // Correct range is renamed, as `surrounding_word` is used to find it.
18791    cx.assert_editor_state(indoc! {"
18792        struct FooRenamedˇ {}
18793    "});
18794}
18795
18796#[gpui::test]
18797async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18798    init_test(cx, |_| {});
18799    let mut cx = EditorTestContext::new(cx).await;
18800
18801    let language = Arc::new(
18802        Language::new(
18803            LanguageConfig::default(),
18804            Some(tree_sitter_html::LANGUAGE.into()),
18805        )
18806        .with_brackets_query(
18807            r#"
18808            ("<" @open "/>" @close)
18809            ("</" @open ">" @close)
18810            ("<" @open ">" @close)
18811            ("\"" @open "\"" @close)
18812            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18813        "#,
18814        )
18815        .unwrap(),
18816    );
18817    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18818
18819    cx.set_state(indoc! {"
18820        <span>ˇ</span>
18821    "});
18822    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18823    cx.assert_editor_state(indoc! {"
18824        <span>
18825        ˇ
18826        </span>
18827    "});
18828
18829    cx.set_state(indoc! {"
18830        <span><span></span>ˇ</span>
18831    "});
18832    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18833    cx.assert_editor_state(indoc! {"
18834        <span><span></span>
18835        ˇ</span>
18836    "});
18837
18838    cx.set_state(indoc! {"
18839        <span>ˇ
18840        </span>
18841    "});
18842    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18843    cx.assert_editor_state(indoc! {"
18844        <span>
18845        ˇ
18846        </span>
18847    "});
18848}
18849
18850#[gpui::test(iterations = 10)]
18851async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18852    init_test(cx, |_| {});
18853
18854    let fs = FakeFs::new(cx.executor());
18855    fs.insert_tree(
18856        path!("/dir"),
18857        json!({
18858            "a.ts": "a",
18859        }),
18860    )
18861    .await;
18862
18863    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18864    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18865    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18866
18867    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18868    language_registry.add(Arc::new(Language::new(
18869        LanguageConfig {
18870            name: "TypeScript".into(),
18871            matcher: LanguageMatcher {
18872                path_suffixes: vec!["ts".to_string()],
18873                ..Default::default()
18874            },
18875            ..Default::default()
18876        },
18877        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18878    )));
18879    let mut fake_language_servers = language_registry.register_fake_lsp(
18880        "TypeScript",
18881        FakeLspAdapter {
18882            capabilities: lsp::ServerCapabilities {
18883                code_lens_provider: Some(lsp::CodeLensOptions {
18884                    resolve_provider: Some(true),
18885                }),
18886                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18887                    commands: vec!["_the/command".to_string()],
18888                    ..lsp::ExecuteCommandOptions::default()
18889                }),
18890                ..lsp::ServerCapabilities::default()
18891            },
18892            ..FakeLspAdapter::default()
18893        },
18894    );
18895
18896    let (buffer, _handle) = project
18897        .update(cx, |p, cx| {
18898            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18899        })
18900        .await
18901        .unwrap();
18902    cx.executor().run_until_parked();
18903
18904    let fake_server = fake_language_servers.next().await.unwrap();
18905
18906    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18907    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18908    drop(buffer_snapshot);
18909    let actions = cx
18910        .update_window(*workspace, |_, window, cx| {
18911            project.code_actions(&buffer, anchor..anchor, window, cx)
18912        })
18913        .unwrap();
18914
18915    fake_server
18916        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18917            Ok(Some(vec![
18918                lsp::CodeLens {
18919                    range: lsp::Range::default(),
18920                    command: Some(lsp::Command {
18921                        title: "Code lens command".to_owned(),
18922                        command: "_the/command".to_owned(),
18923                        arguments: None,
18924                    }),
18925                    data: None,
18926                },
18927                lsp::CodeLens {
18928                    range: lsp::Range::default(),
18929                    command: Some(lsp::Command {
18930                        title: "Command not in capabilities".to_owned(),
18931                        command: "not in capabilities".to_owned(),
18932                        arguments: None,
18933                    }),
18934                    data: None,
18935                },
18936                lsp::CodeLens {
18937                    range: lsp::Range {
18938                        start: lsp::Position {
18939                            line: 1,
18940                            character: 1,
18941                        },
18942                        end: lsp::Position {
18943                            line: 1,
18944                            character: 1,
18945                        },
18946                    },
18947                    command: Some(lsp::Command {
18948                        title: "Command not in range".to_owned(),
18949                        command: "_the/command".to_owned(),
18950                        arguments: None,
18951                    }),
18952                    data: None,
18953                },
18954            ]))
18955        })
18956        .next()
18957        .await;
18958
18959    let actions = actions.await.unwrap();
18960    assert_eq!(
18961        actions.len(),
18962        1,
18963        "Should have only one valid action for the 0..0 range"
18964    );
18965    let action = actions[0].clone();
18966    let apply = project.update(cx, |project, cx| {
18967        project.apply_code_action(buffer.clone(), action, true, cx)
18968    });
18969
18970    // Resolving the code action does not populate its edits. In absence of
18971    // edits, we must execute the given command.
18972    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18973        |mut lens, _| async move {
18974            let lens_command = lens.command.as_mut().expect("should have a command");
18975            assert_eq!(lens_command.title, "Code lens command");
18976            lens_command.arguments = Some(vec![json!("the-argument")]);
18977            Ok(lens)
18978        },
18979    );
18980
18981    // While executing the command, the language server sends the editor
18982    // a `workspaceEdit` request.
18983    fake_server
18984        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18985            let fake = fake_server.clone();
18986            move |params, _| {
18987                assert_eq!(params.command, "_the/command");
18988                let fake = fake.clone();
18989                async move {
18990                    fake.server
18991                        .request::<lsp::request::ApplyWorkspaceEdit>(
18992                            lsp::ApplyWorkspaceEditParams {
18993                                label: None,
18994                                edit: lsp::WorkspaceEdit {
18995                                    changes: Some(
18996                                        [(
18997                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18998                                            vec![lsp::TextEdit {
18999                                                range: lsp::Range::new(
19000                                                    lsp::Position::new(0, 0),
19001                                                    lsp::Position::new(0, 0),
19002                                                ),
19003                                                new_text: "X".into(),
19004                                            }],
19005                                        )]
19006                                        .into_iter()
19007                                        .collect(),
19008                                    ),
19009                                    ..Default::default()
19010                                },
19011                            },
19012                        )
19013                        .await
19014                        .unwrap();
19015                    Ok(Some(json!(null)))
19016                }
19017            }
19018        })
19019        .next()
19020        .await;
19021
19022    // Applying the code lens command returns a project transaction containing the edits
19023    // sent by the language server in its `workspaceEdit` request.
19024    let transaction = apply.await.unwrap();
19025    assert!(transaction.0.contains_key(&buffer));
19026    buffer.update(cx, |buffer, cx| {
19027        assert_eq!(buffer.text(), "Xa");
19028        buffer.undo(cx);
19029        assert_eq!(buffer.text(), "a");
19030    });
19031}
19032
19033#[gpui::test]
19034async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19035    init_test(cx, |_| {});
19036
19037    let fs = FakeFs::new(cx.executor());
19038    let main_text = r#"fn main() {
19039println!("1");
19040println!("2");
19041println!("3");
19042println!("4");
19043println!("5");
19044}"#;
19045    let lib_text = "mod foo {}";
19046    fs.insert_tree(
19047        path!("/a"),
19048        json!({
19049            "lib.rs": lib_text,
19050            "main.rs": main_text,
19051        }),
19052    )
19053    .await;
19054
19055    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19056    let (workspace, cx) =
19057        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19058    let worktree_id = workspace.update(cx, |workspace, cx| {
19059        workspace.project().update(cx, |project, cx| {
19060            project.worktrees(cx).next().unwrap().read(cx).id()
19061        })
19062    });
19063
19064    let expected_ranges = vec![
19065        Point::new(0, 0)..Point::new(0, 0),
19066        Point::new(1, 0)..Point::new(1, 1),
19067        Point::new(2, 0)..Point::new(2, 2),
19068        Point::new(3, 0)..Point::new(3, 3),
19069    ];
19070
19071    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19072    let editor_1 = workspace
19073        .update_in(cx, |workspace, window, cx| {
19074            workspace.open_path(
19075                (worktree_id, "main.rs"),
19076                Some(pane_1.downgrade()),
19077                true,
19078                window,
19079                cx,
19080            )
19081        })
19082        .unwrap()
19083        .await
19084        .downcast::<Editor>()
19085        .unwrap();
19086    pane_1.update(cx, |pane, cx| {
19087        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19088        open_editor.update(cx, |editor, cx| {
19089            assert_eq!(
19090                editor.display_text(cx),
19091                main_text,
19092                "Original main.rs text on initial open",
19093            );
19094            assert_eq!(
19095                editor
19096                    .selections
19097                    .all::<Point>(cx)
19098                    .into_iter()
19099                    .map(|s| s.range())
19100                    .collect::<Vec<_>>(),
19101                vec![Point::zero()..Point::zero()],
19102                "Default selections on initial open",
19103            );
19104        })
19105    });
19106    editor_1.update_in(cx, |editor, window, cx| {
19107        editor.change_selections(None, window, cx, |s| {
19108            s.select_ranges(expected_ranges.clone());
19109        });
19110    });
19111
19112    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19113        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19114    });
19115    let editor_2 = workspace
19116        .update_in(cx, |workspace, window, cx| {
19117            workspace.open_path(
19118                (worktree_id, "main.rs"),
19119                Some(pane_2.downgrade()),
19120                true,
19121                window,
19122                cx,
19123            )
19124        })
19125        .unwrap()
19126        .await
19127        .downcast::<Editor>()
19128        .unwrap();
19129    pane_2.update(cx, |pane, cx| {
19130        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19131        open_editor.update(cx, |editor, cx| {
19132            assert_eq!(
19133                editor.display_text(cx),
19134                main_text,
19135                "Original main.rs text on initial open in another panel",
19136            );
19137            assert_eq!(
19138                editor
19139                    .selections
19140                    .all::<Point>(cx)
19141                    .into_iter()
19142                    .map(|s| s.range())
19143                    .collect::<Vec<_>>(),
19144                vec![Point::zero()..Point::zero()],
19145                "Default selections on initial open in another panel",
19146            );
19147        })
19148    });
19149
19150    editor_2.update_in(cx, |editor, window, cx| {
19151        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19152    });
19153
19154    let _other_editor_1 = workspace
19155        .update_in(cx, |workspace, window, cx| {
19156            workspace.open_path(
19157                (worktree_id, "lib.rs"),
19158                Some(pane_1.downgrade()),
19159                true,
19160                window,
19161                cx,
19162            )
19163        })
19164        .unwrap()
19165        .await
19166        .downcast::<Editor>()
19167        .unwrap();
19168    pane_1
19169        .update_in(cx, |pane, window, cx| {
19170            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19171                .unwrap()
19172        })
19173        .await
19174        .unwrap();
19175    drop(editor_1);
19176    pane_1.update(cx, |pane, cx| {
19177        pane.active_item()
19178            .unwrap()
19179            .downcast::<Editor>()
19180            .unwrap()
19181            .update(cx, |editor, cx| {
19182                assert_eq!(
19183                    editor.display_text(cx),
19184                    lib_text,
19185                    "Other file should be open and active",
19186                );
19187            });
19188        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19189    });
19190
19191    let _other_editor_2 = workspace
19192        .update_in(cx, |workspace, window, cx| {
19193            workspace.open_path(
19194                (worktree_id, "lib.rs"),
19195                Some(pane_2.downgrade()),
19196                true,
19197                window,
19198                cx,
19199            )
19200        })
19201        .unwrap()
19202        .await
19203        .downcast::<Editor>()
19204        .unwrap();
19205    pane_2
19206        .update_in(cx, |pane, window, cx| {
19207            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19208                .unwrap()
19209        })
19210        .await
19211        .unwrap();
19212    drop(editor_2);
19213    pane_2.update(cx, |pane, cx| {
19214        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19215        open_editor.update(cx, |editor, cx| {
19216            assert_eq!(
19217                editor.display_text(cx),
19218                lib_text,
19219                "Other file should be open and active in another panel too",
19220            );
19221        });
19222        assert_eq!(
19223            pane.items().count(),
19224            1,
19225            "No other editors should be open in another pane",
19226        );
19227    });
19228
19229    let _editor_1_reopened = workspace
19230        .update_in(cx, |workspace, window, cx| {
19231            workspace.open_path(
19232                (worktree_id, "main.rs"),
19233                Some(pane_1.downgrade()),
19234                true,
19235                window,
19236                cx,
19237            )
19238        })
19239        .unwrap()
19240        .await
19241        .downcast::<Editor>()
19242        .unwrap();
19243    let _editor_2_reopened = workspace
19244        .update_in(cx, |workspace, window, cx| {
19245            workspace.open_path(
19246                (worktree_id, "main.rs"),
19247                Some(pane_2.downgrade()),
19248                true,
19249                window,
19250                cx,
19251            )
19252        })
19253        .unwrap()
19254        .await
19255        .downcast::<Editor>()
19256        .unwrap();
19257    pane_1.update(cx, |pane, cx| {
19258        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19259        open_editor.update(cx, |editor, cx| {
19260            assert_eq!(
19261                editor.display_text(cx),
19262                main_text,
19263                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19264            );
19265            assert_eq!(
19266                editor
19267                    .selections
19268                    .all::<Point>(cx)
19269                    .into_iter()
19270                    .map(|s| s.range())
19271                    .collect::<Vec<_>>(),
19272                expected_ranges,
19273                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19274            );
19275        })
19276    });
19277    pane_2.update(cx, |pane, cx| {
19278        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19279        open_editor.update(cx, |editor, cx| {
19280            assert_eq!(
19281                editor.display_text(cx),
19282                r#"fn main() {
19283⋯rintln!("1");
19284⋯intln!("2");
19285⋯ntln!("3");
19286println!("4");
19287println!("5");
19288}"#,
19289                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19290            );
19291            assert_eq!(
19292                editor
19293                    .selections
19294                    .all::<Point>(cx)
19295                    .into_iter()
19296                    .map(|s| s.range())
19297                    .collect::<Vec<_>>(),
19298                vec![Point::zero()..Point::zero()],
19299                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19300            );
19301        })
19302    });
19303}
19304
19305#[gpui::test]
19306async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19307    init_test(cx, |_| {});
19308
19309    let fs = FakeFs::new(cx.executor());
19310    let main_text = r#"fn main() {
19311println!("1");
19312println!("2");
19313println!("3");
19314println!("4");
19315println!("5");
19316}"#;
19317    let lib_text = "mod foo {}";
19318    fs.insert_tree(
19319        path!("/a"),
19320        json!({
19321            "lib.rs": lib_text,
19322            "main.rs": main_text,
19323        }),
19324    )
19325    .await;
19326
19327    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19328    let (workspace, cx) =
19329        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19330    let worktree_id = workspace.update(cx, |workspace, cx| {
19331        workspace.project().update(cx, |project, cx| {
19332            project.worktrees(cx).next().unwrap().read(cx).id()
19333        })
19334    });
19335
19336    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19337    let editor = workspace
19338        .update_in(cx, |workspace, window, cx| {
19339            workspace.open_path(
19340                (worktree_id, "main.rs"),
19341                Some(pane.downgrade()),
19342                true,
19343                window,
19344                cx,
19345            )
19346        })
19347        .unwrap()
19348        .await
19349        .downcast::<Editor>()
19350        .unwrap();
19351    pane.update(cx, |pane, cx| {
19352        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19353        open_editor.update(cx, |editor, cx| {
19354            assert_eq!(
19355                editor.display_text(cx),
19356                main_text,
19357                "Original main.rs text on initial open",
19358            );
19359        })
19360    });
19361    editor.update_in(cx, |editor, window, cx| {
19362        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19363    });
19364
19365    cx.update_global(|store: &mut SettingsStore, cx| {
19366        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19367            s.restore_on_file_reopen = Some(false);
19368        });
19369    });
19370    editor.update_in(cx, |editor, window, cx| {
19371        editor.fold_ranges(
19372            vec![
19373                Point::new(1, 0)..Point::new(1, 1),
19374                Point::new(2, 0)..Point::new(2, 2),
19375                Point::new(3, 0)..Point::new(3, 3),
19376            ],
19377            false,
19378            window,
19379            cx,
19380        );
19381    });
19382    pane.update_in(cx, |pane, window, cx| {
19383        pane.close_all_items(&CloseAllItems::default(), window, cx)
19384            .unwrap()
19385    })
19386    .await
19387    .unwrap();
19388    pane.update(cx, |pane, _| {
19389        assert!(pane.active_item().is_none());
19390    });
19391    cx.update_global(|store: &mut SettingsStore, cx| {
19392        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19393            s.restore_on_file_reopen = Some(true);
19394        });
19395    });
19396
19397    let _editor_reopened = workspace
19398        .update_in(cx, |workspace, window, cx| {
19399            workspace.open_path(
19400                (worktree_id, "main.rs"),
19401                Some(pane.downgrade()),
19402                true,
19403                window,
19404                cx,
19405            )
19406        })
19407        .unwrap()
19408        .await
19409        .downcast::<Editor>()
19410        .unwrap();
19411    pane.update(cx, |pane, cx| {
19412        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19413        open_editor.update(cx, |editor, cx| {
19414            assert_eq!(
19415                editor.display_text(cx),
19416                main_text,
19417                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19418            );
19419        })
19420    });
19421}
19422
19423fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19424    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19425    point..point
19426}
19427
19428fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19429    let (text, ranges) = marked_text_ranges(marked_text, true);
19430    assert_eq!(editor.text(cx), text);
19431    assert_eq!(
19432        editor.selections.ranges(cx),
19433        ranges,
19434        "Assert selections are {}",
19435        marked_text
19436    );
19437}
19438
19439pub fn handle_signature_help_request(
19440    cx: &mut EditorLspTestContext,
19441    mocked_response: lsp::SignatureHelp,
19442) -> impl Future<Output = ()> + use<> {
19443    let mut request =
19444        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19445            let mocked_response = mocked_response.clone();
19446            async move { Ok(Some(mocked_response)) }
19447        });
19448
19449    async move {
19450        request.next().await;
19451    }
19452}
19453
19454/// Handle completion request passing a marked string specifying where the completion
19455/// should be triggered from using '|' character, what range should be replaced, and what completions
19456/// should be returned using '<' and '>' to delimit the range.
19457///
19458/// Also see `handle_completion_request_with_insert_and_replace`.
19459#[track_caller]
19460pub fn handle_completion_request(
19461    cx: &mut EditorLspTestContext,
19462    marked_string: &str,
19463    completions: Vec<&'static str>,
19464    counter: Arc<AtomicUsize>,
19465) -> impl Future<Output = ()> {
19466    let complete_from_marker: TextRangeMarker = '|'.into();
19467    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19468    let (_, mut marked_ranges) = marked_text_ranges_by(
19469        marked_string,
19470        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19471    );
19472
19473    let complete_from_position =
19474        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19475    let replace_range =
19476        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19477
19478    let mut request =
19479        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19480            let completions = completions.clone();
19481            counter.fetch_add(1, atomic::Ordering::Release);
19482            async move {
19483                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19484                assert_eq!(
19485                    params.text_document_position.position,
19486                    complete_from_position
19487                );
19488                Ok(Some(lsp::CompletionResponse::Array(
19489                    completions
19490                        .iter()
19491                        .map(|completion_text| lsp::CompletionItem {
19492                            label: completion_text.to_string(),
19493                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19494                                range: replace_range,
19495                                new_text: completion_text.to_string(),
19496                            })),
19497                            ..Default::default()
19498                        })
19499                        .collect(),
19500                )))
19501            }
19502        });
19503
19504    async move {
19505        request.next().await;
19506    }
19507}
19508
19509/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19510/// given instead, which also contains an `insert` range.
19511///
19512/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19513/// that is, `replace_range.start..cursor_pos`.
19514pub fn handle_completion_request_with_insert_and_replace(
19515    cx: &mut EditorLspTestContext,
19516    marked_string: &str,
19517    completions: Vec<&'static str>,
19518    counter: Arc<AtomicUsize>,
19519) -> impl Future<Output = ()> {
19520    let complete_from_marker: TextRangeMarker = '|'.into();
19521    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19522    let (_, mut marked_ranges) = marked_text_ranges_by(
19523        marked_string,
19524        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19525    );
19526
19527    let complete_from_position =
19528        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19529    let replace_range =
19530        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19531
19532    let mut request =
19533        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19534            let completions = completions.clone();
19535            counter.fetch_add(1, atomic::Ordering::Release);
19536            async move {
19537                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19538                assert_eq!(
19539                    params.text_document_position.position, complete_from_position,
19540                    "marker `|` position doesn't match",
19541                );
19542                Ok(Some(lsp::CompletionResponse::Array(
19543                    completions
19544                        .iter()
19545                        .map(|completion_text| lsp::CompletionItem {
19546                            label: completion_text.to_string(),
19547                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19548                                lsp::InsertReplaceEdit {
19549                                    insert: lsp::Range {
19550                                        start: replace_range.start,
19551                                        end: complete_from_position,
19552                                    },
19553                                    replace: replace_range,
19554                                    new_text: completion_text.to_string(),
19555                                },
19556                            )),
19557                            ..Default::default()
19558                        })
19559                        .collect(),
19560                )))
19561            }
19562        });
19563
19564    async move {
19565        request.next().await;
19566    }
19567}
19568
19569fn handle_resolve_completion_request(
19570    cx: &mut EditorLspTestContext,
19571    edits: Option<Vec<(&'static str, &'static str)>>,
19572) -> impl Future<Output = ()> {
19573    let edits = edits.map(|edits| {
19574        edits
19575            .iter()
19576            .map(|(marked_string, new_text)| {
19577                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19578                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19579                lsp::TextEdit::new(replace_range, new_text.to_string())
19580            })
19581            .collect::<Vec<_>>()
19582    });
19583
19584    let mut request =
19585        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19586            let edits = edits.clone();
19587            async move {
19588                Ok(lsp::CompletionItem {
19589                    additional_text_edits: edits,
19590                    ..Default::default()
19591                })
19592            }
19593        });
19594
19595    async move {
19596        request.next().await;
19597    }
19598}
19599
19600pub(crate) fn update_test_language_settings(
19601    cx: &mut TestAppContext,
19602    f: impl Fn(&mut AllLanguageSettingsContent),
19603) {
19604    cx.update(|cx| {
19605        SettingsStore::update_global(cx, |store, cx| {
19606            store.update_user_settings::<AllLanguageSettings>(cx, f);
19607        });
19608    });
19609}
19610
19611pub(crate) fn update_test_project_settings(
19612    cx: &mut TestAppContext,
19613    f: impl Fn(&mut ProjectSettings),
19614) {
19615    cx.update(|cx| {
19616        SettingsStore::update_global(cx, |store, cx| {
19617            store.update_user_settings::<ProjectSettings>(cx, f);
19618        });
19619    });
19620}
19621
19622pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19623    cx.update(|cx| {
19624        assets::Assets.load_test_fonts(cx);
19625        let store = SettingsStore::test(cx);
19626        cx.set_global(store);
19627        theme::init(theme::LoadThemes::JustBase, cx);
19628        release_channel::init(SemanticVersion::default(), cx);
19629        client::init_settings(cx);
19630        language::init(cx);
19631        Project::init_settings(cx);
19632        workspace::init_settings(cx);
19633        crate::init(cx);
19634    });
19635
19636    update_test_language_settings(cx, f);
19637}
19638
19639#[track_caller]
19640fn assert_hunk_revert(
19641    not_reverted_text_with_selections: &str,
19642    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19643    expected_reverted_text_with_selections: &str,
19644    base_text: &str,
19645    cx: &mut EditorLspTestContext,
19646) {
19647    cx.set_state(not_reverted_text_with_selections);
19648    cx.set_head_text(base_text);
19649    cx.executor().run_until_parked();
19650
19651    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19652        let snapshot = editor.snapshot(window, cx);
19653        let reverted_hunk_statuses = snapshot
19654            .buffer_snapshot
19655            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19656            .map(|hunk| hunk.status().kind)
19657            .collect::<Vec<_>>();
19658
19659        editor.git_restore(&Default::default(), window, cx);
19660        reverted_hunk_statuses
19661    });
19662    cx.executor().run_until_parked();
19663    cx.assert_editor_state(expected_reverted_text_with_selections);
19664    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19665}