editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.defaults.tab_size = NonZeroU32::new(4)
 2924    });
 2925
 2926    let language = Arc::new(
 2927        Language::new(
 2928            LanguageConfig::default(),
 2929            Some(tree_sitter_rust::LANGUAGE.into()),
 2930        )
 2931        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2932        .unwrap(),
 2933    );
 2934
 2935    let mut cx = EditorTestContext::new(cx).await;
 2936    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2937    cx.set_state(indoc! {"
 2938        fn a() {
 2939            if b {
 2940        \t ˇc
 2941            }
 2942        }
 2943    "});
 2944
 2945    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2946    cx.assert_editor_state(indoc! {"
 2947        fn a() {
 2948            if b {
 2949                ˇc
 2950            }
 2951        }
 2952    "});
 2953}
 2954
 2955#[gpui::test]
 2956async fn test_indent_outdent(cx: &mut TestAppContext) {
 2957    init_test(cx, |settings| {
 2958        settings.defaults.tab_size = NonZeroU32::new(4);
 2959    });
 2960
 2961    let mut cx = EditorTestContext::new(cx).await;
 2962
 2963    cx.set_state(indoc! {"
 2964          «oneˇ» «twoˇ»
 2965        three
 2966         four
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970            «oneˇ» «twoˇ»
 2971        three
 2972         four
 2973    "});
 2974
 2975    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2976    cx.assert_editor_state(indoc! {"
 2977        «oneˇ» «twoˇ»
 2978        three
 2979         four
 2980    "});
 2981
 2982    // select across line ending
 2983    cx.set_state(indoc! {"
 2984        one two
 2985        t«hree
 2986        ˇ» four
 2987    "});
 2988    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2989    cx.assert_editor_state(indoc! {"
 2990        one two
 2991            t«hree
 2992        ˇ» four
 2993    "});
 2994
 2995    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2996    cx.assert_editor_state(indoc! {"
 2997        one two
 2998        t«hree
 2999        ˇ» four
 3000    "});
 3001
 3002    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3003    cx.set_state(indoc! {"
 3004        one two
 3005        ˇthree
 3006            four
 3007    "});
 3008    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3009    cx.assert_editor_state(indoc! {"
 3010        one two
 3011            ˇthree
 3012            four
 3013    "});
 3014
 3015    cx.set_state(indoc! {"
 3016        one two
 3017        ˇ    three
 3018            four
 3019    "});
 3020    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3021    cx.assert_editor_state(indoc! {"
 3022        one two
 3023        ˇthree
 3024            four
 3025    "});
 3026}
 3027
 3028#[gpui::test]
 3029async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3030    init_test(cx, |settings| {
 3031        settings.defaults.hard_tabs = Some(true);
 3032    });
 3033
 3034    let mut cx = EditorTestContext::new(cx).await;
 3035
 3036    // select two ranges on one line
 3037    cx.set_state(indoc! {"
 3038        «oneˇ» «twoˇ»
 3039        three
 3040        four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        \t«oneˇ» «twoˇ»
 3045        three
 3046        four
 3047    "});
 3048    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        \t\t«oneˇ» «twoˇ»
 3051        three
 3052        four
 3053    "});
 3054    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3055    cx.assert_editor_state(indoc! {"
 3056        \t«oneˇ» «twoˇ»
 3057        three
 3058        four
 3059    "});
 3060    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3061    cx.assert_editor_state(indoc! {"
 3062        «oneˇ» «twoˇ»
 3063        three
 3064        four
 3065    "});
 3066
 3067    // select across a line ending
 3068    cx.set_state(indoc! {"
 3069        one two
 3070        t«hree
 3071        ˇ»four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        one two
 3076        \tt«hree
 3077        ˇ»four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        one two
 3082        \t\tt«hree
 3083        ˇ»four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        one two
 3088        \tt«hree
 3089        ˇ»four
 3090    "});
 3091    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3092    cx.assert_editor_state(indoc! {"
 3093        one two
 3094        t«hree
 3095        ˇ»four
 3096    "});
 3097
 3098    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3099    cx.set_state(indoc! {"
 3100        one two
 3101        ˇthree
 3102        four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        ˇthree
 3108        four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        \tˇthree
 3114        four
 3115    "});
 3116    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3117    cx.assert_editor_state(indoc! {"
 3118        one two
 3119        ˇthree
 3120        four
 3121    "});
 3122}
 3123
 3124#[gpui::test]
 3125fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3126    init_test(cx, |settings| {
 3127        settings.languages.extend([
 3128            (
 3129                "TOML".into(),
 3130                LanguageSettingsContent {
 3131                    tab_size: NonZeroU32::new(2),
 3132                    ..Default::default()
 3133                },
 3134            ),
 3135            (
 3136                "Rust".into(),
 3137                LanguageSettingsContent {
 3138                    tab_size: NonZeroU32::new(4),
 3139                    ..Default::default()
 3140                },
 3141            ),
 3142        ]);
 3143    });
 3144
 3145    let toml_language = Arc::new(Language::new(
 3146        LanguageConfig {
 3147            name: "TOML".into(),
 3148            ..Default::default()
 3149        },
 3150        None,
 3151    ));
 3152    let rust_language = Arc::new(Language::new(
 3153        LanguageConfig {
 3154            name: "Rust".into(),
 3155            ..Default::default()
 3156        },
 3157        None,
 3158    ));
 3159
 3160    let toml_buffer =
 3161        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3162    let rust_buffer =
 3163        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3164    let multibuffer = cx.new(|cx| {
 3165        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3166        multibuffer.push_excerpts(
 3167            toml_buffer.clone(),
 3168            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3169            cx,
 3170        );
 3171        multibuffer.push_excerpts(
 3172            rust_buffer.clone(),
 3173            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3174            cx,
 3175        );
 3176        multibuffer
 3177    });
 3178
 3179    cx.add_window(|window, cx| {
 3180        let mut editor = build_editor(multibuffer, window, cx);
 3181
 3182        assert_eq!(
 3183            editor.text(cx),
 3184            indoc! {"
 3185                a = 1
 3186                b = 2
 3187
 3188                const c: usize = 3;
 3189            "}
 3190        );
 3191
 3192        select_ranges(
 3193            &mut editor,
 3194            indoc! {"
 3195                «aˇ» = 1
 3196                b = 2
 3197
 3198                «const c:ˇ» usize = 3;
 3199            "},
 3200            window,
 3201            cx,
 3202        );
 3203
 3204        editor.tab(&Tab, window, cx);
 3205        assert_text_with_selections(
 3206            &mut editor,
 3207            indoc! {"
 3208                  «aˇ» = 1
 3209                b = 2
 3210
 3211                    «const c:ˇ» usize = 3;
 3212            "},
 3213            cx,
 3214        );
 3215        editor.backtab(&Backtab, window, cx);
 3216        assert_text_with_selections(
 3217            &mut editor,
 3218            indoc! {"
 3219                «aˇ» = 1
 3220                b = 2
 3221
 3222                «const c:ˇ» usize = 3;
 3223            "},
 3224            cx,
 3225        );
 3226
 3227        editor
 3228    });
 3229}
 3230
 3231#[gpui::test]
 3232async fn test_backspace(cx: &mut TestAppContext) {
 3233    init_test(cx, |_| {});
 3234
 3235    let mut cx = EditorTestContext::new(cx).await;
 3236
 3237    // Basic backspace
 3238    cx.set_state(indoc! {"
 3239        onˇe two three
 3240        fou«rˇ» five six
 3241        seven «ˇeight nine
 3242        »ten
 3243    "});
 3244    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3245    cx.assert_editor_state(indoc! {"
 3246        oˇe two three
 3247        fouˇ five six
 3248        seven ˇten
 3249    "});
 3250
 3251    // Test backspace inside and around indents
 3252    cx.set_state(indoc! {"
 3253        zero
 3254            ˇone
 3255                ˇtwo
 3256            ˇ ˇ ˇ  three
 3257        ˇ  ˇ  four
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        zero
 3262        ˇone
 3263            ˇtwo
 3264        ˇ  threeˇ  four
 3265    "});
 3266}
 3267
 3268#[gpui::test]
 3269async fn test_delete(cx: &mut TestAppContext) {
 3270    init_test(cx, |_| {});
 3271
 3272    let mut cx = EditorTestContext::new(cx).await;
 3273    cx.set_state(indoc! {"
 3274        onˇe two three
 3275        fou«rˇ» five six
 3276        seven «ˇeight nine
 3277        »ten
 3278    "});
 3279    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3280    cx.assert_editor_state(indoc! {"
 3281        onˇ two three
 3282        fouˇ five six
 3283        seven ˇten
 3284    "});
 3285}
 3286
 3287#[gpui::test]
 3288fn test_delete_line(cx: &mut TestAppContext) {
 3289    init_test(cx, |_| {});
 3290
 3291    let editor = cx.add_window(|window, cx| {
 3292        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3293        build_editor(buffer, window, cx)
 3294    });
 3295    _ = editor.update(cx, |editor, window, cx| {
 3296        editor.change_selections(None, window, cx, |s| {
 3297            s.select_display_ranges([
 3298                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3299                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3300                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3301            ])
 3302        });
 3303        editor.delete_line(&DeleteLine, window, cx);
 3304        assert_eq!(editor.display_text(cx), "ghi");
 3305        assert_eq!(
 3306            editor.selections.display_ranges(cx),
 3307            vec![
 3308                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3309                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3310            ]
 3311        );
 3312    });
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3322            ])
 3323        });
 3324        editor.delete_line(&DeleteLine, window, cx);
 3325        assert_eq!(editor.display_text(cx), "ghi\n");
 3326        assert_eq!(
 3327            editor.selections.display_ranges(cx),
 3328            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3329        );
 3330    });
 3331}
 3332
 3333#[gpui::test]
 3334fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3335    init_test(cx, |_| {});
 3336
 3337    cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3339        let mut editor = build_editor(buffer.clone(), window, cx);
 3340        let buffer = buffer.read(cx).as_singleton().unwrap();
 3341
 3342        assert_eq!(
 3343            editor.selections.ranges::<Point>(cx),
 3344            &[Point::new(0, 0)..Point::new(0, 0)]
 3345        );
 3346
 3347        // When on single line, replace newline at end by space
 3348        editor.join_lines(&JoinLines, window, cx);
 3349        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3350        assert_eq!(
 3351            editor.selections.ranges::<Point>(cx),
 3352            &[Point::new(0, 3)..Point::new(0, 3)]
 3353        );
 3354
 3355        // When multiple lines are selected, remove newlines that are spanned by the selection
 3356        editor.change_selections(None, window, cx, |s| {
 3357            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3358        });
 3359        editor.join_lines(&JoinLines, window, cx);
 3360        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3361        assert_eq!(
 3362            editor.selections.ranges::<Point>(cx),
 3363            &[Point::new(0, 11)..Point::new(0, 11)]
 3364        );
 3365
 3366        // Undo should be transactional
 3367        editor.undo(&Undo, window, cx);
 3368        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3369        assert_eq!(
 3370            editor.selections.ranges::<Point>(cx),
 3371            &[Point::new(0, 5)..Point::new(2, 2)]
 3372        );
 3373
 3374        // When joining an empty line don't insert a space
 3375        editor.change_selections(None, window, cx, |s| {
 3376            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3377        });
 3378        editor.join_lines(&JoinLines, window, cx);
 3379        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3380        assert_eq!(
 3381            editor.selections.ranges::<Point>(cx),
 3382            [Point::new(2, 3)..Point::new(2, 3)]
 3383        );
 3384
 3385        // We can remove trailing newlines
 3386        editor.join_lines(&JoinLines, window, cx);
 3387        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3388        assert_eq!(
 3389            editor.selections.ranges::<Point>(cx),
 3390            [Point::new(2, 3)..Point::new(2, 3)]
 3391        );
 3392
 3393        // We don't blow up on the last line
 3394        editor.join_lines(&JoinLines, window, cx);
 3395        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3396        assert_eq!(
 3397            editor.selections.ranges::<Point>(cx),
 3398            [Point::new(2, 3)..Point::new(2, 3)]
 3399        );
 3400
 3401        // reset to test indentation
 3402        editor.buffer.update(cx, |buffer, cx| {
 3403            buffer.edit(
 3404                [
 3405                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3406                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3407                ],
 3408                None,
 3409                cx,
 3410            )
 3411        });
 3412
 3413        // We remove any leading spaces
 3414        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3415        editor.change_selections(None, window, cx, |s| {
 3416            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3417        });
 3418        editor.join_lines(&JoinLines, window, cx);
 3419        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3420
 3421        // We don't insert a space for a line containing only spaces
 3422        editor.join_lines(&JoinLines, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3424
 3425        // We ignore any leading tabs
 3426        editor.join_lines(&JoinLines, window, cx);
 3427        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3428
 3429        editor
 3430    });
 3431}
 3432
 3433#[gpui::test]
 3434fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3435    init_test(cx, |_| {});
 3436
 3437    cx.add_window(|window, cx| {
 3438        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3439        let mut editor = build_editor(buffer.clone(), window, cx);
 3440        let buffer = buffer.read(cx).as_singleton().unwrap();
 3441
 3442        editor.change_selections(None, window, cx, |s| {
 3443            s.select_ranges([
 3444                Point::new(0, 2)..Point::new(1, 1),
 3445                Point::new(1, 2)..Point::new(1, 2),
 3446                Point::new(3, 1)..Point::new(3, 2),
 3447            ])
 3448        });
 3449
 3450        editor.join_lines(&JoinLines, window, cx);
 3451        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3452
 3453        assert_eq!(
 3454            editor.selections.ranges::<Point>(cx),
 3455            [
 3456                Point::new(0, 7)..Point::new(0, 7),
 3457                Point::new(1, 3)..Point::new(1, 3)
 3458            ]
 3459        );
 3460        editor
 3461    });
 3462}
 3463
 3464#[gpui::test]
 3465async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3466    init_test(cx, |_| {});
 3467
 3468    let mut cx = EditorTestContext::new(cx).await;
 3469
 3470    let diff_base = r#"
 3471        Line 0
 3472        Line 1
 3473        Line 2
 3474        Line 3
 3475        "#
 3476    .unindent();
 3477
 3478    cx.set_state(
 3479        &r#"
 3480        ˇLine 0
 3481        Line 1
 3482        Line 2
 3483        Line 3
 3484        "#
 3485        .unindent(),
 3486    );
 3487
 3488    cx.set_head_text(&diff_base);
 3489    executor.run_until_parked();
 3490
 3491    // Join lines
 3492    cx.update_editor(|editor, window, cx| {
 3493        editor.join_lines(&JoinLines, window, cx);
 3494    });
 3495    executor.run_until_parked();
 3496
 3497    cx.assert_editor_state(
 3498        &r#"
 3499        Line 0ˇ Line 1
 3500        Line 2
 3501        Line 3
 3502        "#
 3503        .unindent(),
 3504    );
 3505    // Join again
 3506    cx.update_editor(|editor, window, cx| {
 3507        editor.join_lines(&JoinLines, window, cx);
 3508    });
 3509    executor.run_until_parked();
 3510
 3511    cx.assert_editor_state(
 3512        &r#"
 3513        Line 0 Line 1ˇ Line 2
 3514        Line 3
 3515        "#
 3516        .unindent(),
 3517    );
 3518}
 3519
 3520#[gpui::test]
 3521async fn test_custom_newlines_cause_no_false_positive_diffs(
 3522    executor: BackgroundExecutor,
 3523    cx: &mut TestAppContext,
 3524) {
 3525    init_test(cx, |_| {});
 3526    let mut cx = EditorTestContext::new(cx).await;
 3527    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3528    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3529    executor.run_until_parked();
 3530
 3531    cx.update_editor(|editor, window, cx| {
 3532        let snapshot = editor.snapshot(window, cx);
 3533        assert_eq!(
 3534            snapshot
 3535                .buffer_snapshot
 3536                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3537                .collect::<Vec<_>>(),
 3538            Vec::new(),
 3539            "Should not have any diffs for files with custom newlines"
 3540        );
 3541    });
 3542}
 3543
 3544#[gpui::test]
 3545async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3546    init_test(cx, |_| {});
 3547
 3548    let mut cx = EditorTestContext::new(cx).await;
 3549
 3550    // Test sort_lines_case_insensitive()
 3551    cx.set_state(indoc! {"
 3552        «z
 3553        y
 3554        x
 3555        Z
 3556        Y
 3557        Xˇ»
 3558    "});
 3559    cx.update_editor(|e, window, cx| {
 3560        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3561    });
 3562    cx.assert_editor_state(indoc! {"
 3563        «x
 3564        X
 3565        y
 3566        Y
 3567        z
 3568        Zˇ»
 3569    "});
 3570
 3571    // Test reverse_lines()
 3572    cx.set_state(indoc! {"
 3573        «5
 3574        4
 3575        3
 3576        2
 3577        1ˇ»
 3578    "});
 3579    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «1
 3582        2
 3583        3
 3584        4
 3585        5ˇ»
 3586    "});
 3587
 3588    // Skip testing shuffle_line()
 3589
 3590    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3591    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3592
 3593    // Don't manipulate when cursor is on single line, but expand the selection
 3594    cx.set_state(indoc! {"
 3595        ddˇdd
 3596        ccc
 3597        bb
 3598        a
 3599    "});
 3600    cx.update_editor(|e, window, cx| {
 3601        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3602    });
 3603    cx.assert_editor_state(indoc! {"
 3604        «ddddˇ»
 3605        ccc
 3606        bb
 3607        a
 3608    "});
 3609
 3610    // Basic manipulate case
 3611    // Start selection moves to column 0
 3612    // End of selection shrinks to fit shorter line
 3613    cx.set_state(indoc! {"
 3614        dd«d
 3615        ccc
 3616        bb
 3617        aaaaaˇ»
 3618    "});
 3619    cx.update_editor(|e, window, cx| {
 3620        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3621    });
 3622    cx.assert_editor_state(indoc! {"
 3623        «aaaaa
 3624        bb
 3625        ccc
 3626        dddˇ»
 3627    "});
 3628
 3629    // Manipulate case with newlines
 3630    cx.set_state(indoc! {"
 3631        dd«d
 3632        ccc
 3633
 3634        bb
 3635        aaaaa
 3636
 3637        ˇ»
 3638    "});
 3639    cx.update_editor(|e, window, cx| {
 3640        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3641    });
 3642    cx.assert_editor_state(indoc! {"
 3643        «
 3644
 3645        aaaaa
 3646        bb
 3647        ccc
 3648        dddˇ»
 3649
 3650    "});
 3651
 3652    // Adding new line
 3653    cx.set_state(indoc! {"
 3654        aa«a
 3655        bbˇ»b
 3656    "});
 3657    cx.update_editor(|e, window, cx| {
 3658        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3659    });
 3660    cx.assert_editor_state(indoc! {"
 3661        «aaa
 3662        bbb
 3663        added_lineˇ»
 3664    "});
 3665
 3666    // Removing line
 3667    cx.set_state(indoc! {"
 3668        aa«a
 3669        bbbˇ»
 3670    "});
 3671    cx.update_editor(|e, window, cx| {
 3672        e.manipulate_lines(window, cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «aaaˇ»
 3678    "});
 3679
 3680    // Removing all lines
 3681    cx.set_state(indoc! {"
 3682        aa«a
 3683        bbbˇ»
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.manipulate_lines(window, cx, |lines| {
 3687            lines.drain(..);
 3688        })
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        ˇ
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Consider continuous selection as single selection
 3702    cx.set_state(indoc! {"
 3703        Aaa«aa
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «Aaaaa
 3713        ccc
 3714        bb
 3715        aaaaaˇ»
 3716    "});
 3717
 3718    cx.set_state(indoc! {"
 3719        Aaa«aa
 3720        cˇ»c«c
 3721        bb
 3722        aaaˇ»aa
 3723    "});
 3724    cx.update_editor(|e, window, cx| {
 3725        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3726    });
 3727    cx.assert_editor_state(indoc! {"
 3728        «Aaaaa
 3729        ccc
 3730        bbˇ»
 3731    "});
 3732
 3733    // Consider non continuous selection as distinct dedup operations
 3734    cx.set_state(indoc! {"
 3735        «aaaaa
 3736        bb
 3737        aaaaa
 3738        aaaaaˇ»
 3739
 3740        aaa«aaˇ»
 3741    "});
 3742    cx.update_editor(|e, window, cx| {
 3743        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        «aaaaa
 3747        bbˇ»
 3748
 3749        «aaaaaˇ»
 3750    "});
 3751}
 3752
 3753#[gpui::test]
 3754async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3755    init_test(cx, |_| {});
 3756
 3757    let mut cx = EditorTestContext::new(cx).await;
 3758
 3759    cx.set_state(indoc! {"
 3760        «Aaa
 3761        aAa
 3762        Aaaˇ»
 3763    "});
 3764    cx.update_editor(|e, window, cx| {
 3765        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3766    });
 3767    cx.assert_editor_state(indoc! {"
 3768        «Aaa
 3769        aAaˇ»
 3770    "});
 3771
 3772    cx.set_state(indoc! {"
 3773        «Aaa
 3774        aAa
 3775        aaAˇ»
 3776    "});
 3777    cx.update_editor(|e, window, cx| {
 3778        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3779    });
 3780    cx.assert_editor_state(indoc! {"
 3781        «Aaaˇ»
 3782    "});
 3783}
 3784
 3785#[gpui::test]
 3786async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3787    init_test(cx, |_| {});
 3788
 3789    let mut cx = EditorTestContext::new(cx).await;
 3790
 3791    // Manipulate with multiple selections on a single line
 3792    cx.set_state(indoc! {"
 3793        dd«dd
 3794        cˇ»c«c
 3795        bb
 3796        aaaˇ»aa
 3797    "});
 3798    cx.update_editor(|e, window, cx| {
 3799        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3800    });
 3801    cx.assert_editor_state(indoc! {"
 3802        «aaaaa
 3803        bb
 3804        ccc
 3805        ddddˇ»
 3806    "});
 3807
 3808    // Manipulate with multiple disjoin selections
 3809    cx.set_state(indoc! {"
 3810 3811        4
 3812        3
 3813        2
 3814        1ˇ»
 3815
 3816        dd«dd
 3817        ccc
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «1
 3826        2
 3827        3
 3828        4
 3829        5ˇ»
 3830
 3831        «aaaaa
 3832        bb
 3833        ccc
 3834        ddddˇ»
 3835    "});
 3836
 3837    // Adding lines on each selection
 3838    cx.set_state(indoc! {"
 3839 3840        1ˇ»
 3841
 3842        bb«bb
 3843        aaaˇ»aa
 3844    "});
 3845    cx.update_editor(|e, window, cx| {
 3846        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3847    });
 3848    cx.assert_editor_state(indoc! {"
 3849        «2
 3850        1
 3851        added lineˇ»
 3852
 3853        «bbbb
 3854        aaaaa
 3855        added lineˇ»
 3856    "});
 3857
 3858    // Removing lines on each selection
 3859    cx.set_state(indoc! {"
 3860 3861        1ˇ»
 3862
 3863        bb«bb
 3864        aaaˇ»aa
 3865    "});
 3866    cx.update_editor(|e, window, cx| {
 3867        e.manipulate_lines(window, cx, |lines| {
 3868            lines.pop();
 3869        })
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2ˇ»
 3873
 3874        «bbbbˇ»
 3875    "});
 3876}
 3877
 3878#[gpui::test]
 3879async fn test_manipulate_text(cx: &mut TestAppContext) {
 3880    init_test(cx, |_| {});
 3881
 3882    let mut cx = EditorTestContext::new(cx).await;
 3883
 3884    // Test convert_to_upper_case()
 3885    cx.set_state(indoc! {"
 3886        «hello worldˇ»
 3887    "});
 3888    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3889    cx.assert_editor_state(indoc! {"
 3890        «HELLO WORLDˇ»
 3891    "});
 3892
 3893    // Test convert_to_lower_case()
 3894    cx.set_state(indoc! {"
 3895        «HELLO WORLDˇ»
 3896    "});
 3897    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3898    cx.assert_editor_state(indoc! {"
 3899        «hello worldˇ»
 3900    "});
 3901
 3902    // Test multiple line, single selection case
 3903    cx.set_state(indoc! {"
 3904        «The quick brown
 3905        fox jumps over
 3906        the lazy dogˇ»
 3907    "});
 3908    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3909    cx.assert_editor_state(indoc! {"
 3910        «The Quick Brown
 3911        Fox Jumps Over
 3912        The Lazy Dogˇ»
 3913    "});
 3914
 3915    // Test multiple line, single selection case
 3916    cx.set_state(indoc! {"
 3917        «The quick brown
 3918        fox jumps over
 3919        the lazy dogˇ»
 3920    "});
 3921    cx.update_editor(|e, window, cx| {
 3922        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3923    });
 3924    cx.assert_editor_state(indoc! {"
 3925        «TheQuickBrown
 3926        FoxJumpsOver
 3927        TheLazyDogˇ»
 3928    "});
 3929
 3930    // From here on out, test more complex cases of manipulate_text()
 3931
 3932    // Test no selection case - should affect words cursors are in
 3933    // Cursor at beginning, middle, and end of word
 3934    cx.set_state(indoc! {"
 3935        ˇhello big beauˇtiful worldˇ
 3936    "});
 3937    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3938    cx.assert_editor_state(indoc! {"
 3939        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3940    "});
 3941
 3942    // Test multiple selections on a single line and across multiple lines
 3943    cx.set_state(indoc! {"
 3944        «Theˇ» quick «brown
 3945        foxˇ» jumps «overˇ»
 3946        the «lazyˇ» dog
 3947    "});
 3948    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3949    cx.assert_editor_state(indoc! {"
 3950        «THEˇ» quick «BROWN
 3951        FOXˇ» jumps «OVERˇ»
 3952        the «LAZYˇ» dog
 3953    "});
 3954
 3955    // Test case where text length grows
 3956    cx.set_state(indoc! {"
 3957        «tschüߡ»
 3958    "});
 3959    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3960    cx.assert_editor_state(indoc! {"
 3961        «TSCHÜSSˇ»
 3962    "});
 3963
 3964    // Test to make sure we don't crash when text shrinks
 3965    cx.set_state(indoc! {"
 3966        aaa_bbbˇ
 3967    "});
 3968    cx.update_editor(|e, window, cx| {
 3969        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3970    });
 3971    cx.assert_editor_state(indoc! {"
 3972        «aaaBbbˇ»
 3973    "});
 3974
 3975    // Test to make sure we all aware of the fact that each word can grow and shrink
 3976    // Final selections should be aware of this fact
 3977    cx.set_state(indoc! {"
 3978        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3979    "});
 3980    cx.update_editor(|e, window, cx| {
 3981        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3982    });
 3983    cx.assert_editor_state(indoc! {"
 3984        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3985    "});
 3986
 3987    cx.set_state(indoc! {"
 3988        «hElLo, WoRld!ˇ»
 3989    "});
 3990    cx.update_editor(|e, window, cx| {
 3991        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3992    });
 3993    cx.assert_editor_state(indoc! {"
 3994        «HeLlO, wOrLD!ˇ»
 3995    "});
 3996}
 3997
 3998#[gpui::test]
 3999fn test_duplicate_line(cx: &mut TestAppContext) {
 4000    init_test(cx, |_| {});
 4001
 4002    let editor = cx.add_window(|window, cx| {
 4003        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4004        build_editor(buffer, window, cx)
 4005    });
 4006    _ = editor.update(cx, |editor, window, cx| {
 4007        editor.change_selections(None, window, cx, |s| {
 4008            s.select_display_ranges([
 4009                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4010                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4011                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4012                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4013            ])
 4014        });
 4015        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4016        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4017        assert_eq!(
 4018            editor.selections.display_ranges(cx),
 4019            vec![
 4020                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4021                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4022                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4023                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4024            ]
 4025        );
 4026    });
 4027
 4028    let editor = cx.add_window(|window, cx| {
 4029        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4030        build_editor(buffer, window, cx)
 4031    });
 4032    _ = editor.update(cx, |editor, window, cx| {
 4033        editor.change_selections(None, window, cx, |s| {
 4034            s.select_display_ranges([
 4035                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4036                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4037            ])
 4038        });
 4039        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4040        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4041        assert_eq!(
 4042            editor.selections.display_ranges(cx),
 4043            vec![
 4044                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4045                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4046            ]
 4047        );
 4048    });
 4049
 4050    // With `move_upwards` the selections stay in place, except for
 4051    // the lines inserted above them
 4052    let editor = cx.add_window(|window, cx| {
 4053        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4054        build_editor(buffer, window, cx)
 4055    });
 4056    _ = editor.update(cx, |editor, window, cx| {
 4057        editor.change_selections(None, window, cx, |s| {
 4058            s.select_display_ranges([
 4059                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4060                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4061                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4062                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4063            ])
 4064        });
 4065        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4066        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4067        assert_eq!(
 4068            editor.selections.display_ranges(cx),
 4069            vec![
 4070                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4071                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4072                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4073                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4074            ]
 4075        );
 4076    });
 4077
 4078    let editor = cx.add_window(|window, cx| {
 4079        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4080        build_editor(buffer, window, cx)
 4081    });
 4082    _ = editor.update(cx, |editor, window, cx| {
 4083        editor.change_selections(None, window, cx, |s| {
 4084            s.select_display_ranges([
 4085                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4086                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4087            ])
 4088        });
 4089        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4090        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4091        assert_eq!(
 4092            editor.selections.display_ranges(cx),
 4093            vec![
 4094                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4095                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4096            ]
 4097        );
 4098    });
 4099
 4100    let editor = cx.add_window(|window, cx| {
 4101        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4102        build_editor(buffer, window, cx)
 4103    });
 4104    _ = editor.update(cx, |editor, window, cx| {
 4105        editor.change_selections(None, window, cx, |s| {
 4106            s.select_display_ranges([
 4107                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4108                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4109            ])
 4110        });
 4111        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4112        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4113        assert_eq!(
 4114            editor.selections.display_ranges(cx),
 4115            vec![
 4116                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4117                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4118            ]
 4119        );
 4120    });
 4121}
 4122
 4123#[gpui::test]
 4124fn test_move_line_up_down(cx: &mut TestAppContext) {
 4125    init_test(cx, |_| {});
 4126
 4127    let editor = cx.add_window(|window, cx| {
 4128        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4129        build_editor(buffer, window, cx)
 4130    });
 4131    _ = editor.update(cx, |editor, window, cx| {
 4132        editor.fold_creases(
 4133            vec![
 4134                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4135                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4136                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4137            ],
 4138            true,
 4139            window,
 4140            cx,
 4141        );
 4142        editor.change_selections(None, window, cx, |s| {
 4143            s.select_display_ranges([
 4144                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4145                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4146                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4147                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4148            ])
 4149        });
 4150        assert_eq!(
 4151            editor.display_text(cx),
 4152            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4153        );
 4154
 4155        editor.move_line_up(&MoveLineUp, window, cx);
 4156        assert_eq!(
 4157            editor.display_text(cx),
 4158            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4159        );
 4160        assert_eq!(
 4161            editor.selections.display_ranges(cx),
 4162            vec![
 4163                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4164                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4165                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4166                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4167            ]
 4168        );
 4169    });
 4170
 4171    _ = editor.update(cx, |editor, window, cx| {
 4172        editor.move_line_down(&MoveLineDown, window, cx);
 4173        assert_eq!(
 4174            editor.display_text(cx),
 4175            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4176        );
 4177        assert_eq!(
 4178            editor.selections.display_ranges(cx),
 4179            vec![
 4180                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4181                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4182                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4183                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4184            ]
 4185        );
 4186    });
 4187
 4188    _ = editor.update(cx, |editor, window, cx| {
 4189        editor.move_line_down(&MoveLineDown, window, cx);
 4190        assert_eq!(
 4191            editor.display_text(cx),
 4192            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4193        );
 4194        assert_eq!(
 4195            editor.selections.display_ranges(cx),
 4196            vec![
 4197                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4198                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4199                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4200                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4201            ]
 4202        );
 4203    });
 4204
 4205    _ = editor.update(cx, |editor, window, cx| {
 4206        editor.move_line_up(&MoveLineUp, window, cx);
 4207        assert_eq!(
 4208            editor.display_text(cx),
 4209            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4210        );
 4211        assert_eq!(
 4212            editor.selections.display_ranges(cx),
 4213            vec![
 4214                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4215                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4216                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4217                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4218            ]
 4219        );
 4220    });
 4221}
 4222
 4223#[gpui::test]
 4224fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4225    init_test(cx, |_| {});
 4226
 4227    let editor = cx.add_window(|window, cx| {
 4228        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4229        build_editor(buffer, window, cx)
 4230    });
 4231    _ = editor.update(cx, |editor, window, cx| {
 4232        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4233        editor.insert_blocks(
 4234            [BlockProperties {
 4235                style: BlockStyle::Fixed,
 4236                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4237                height: Some(1),
 4238                render: Arc::new(|_| div().into_any()),
 4239                priority: 0,
 4240            }],
 4241            Some(Autoscroll::fit()),
 4242            cx,
 4243        );
 4244        editor.change_selections(None, window, cx, |s| {
 4245            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4246        });
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248    });
 4249}
 4250
 4251#[gpui::test]
 4252async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4253    init_test(cx, |_| {});
 4254
 4255    let mut cx = EditorTestContext::new(cx).await;
 4256    cx.set_state(
 4257        &"
 4258            ˇzero
 4259            one
 4260            two
 4261            three
 4262            four
 4263            five
 4264        "
 4265        .unindent(),
 4266    );
 4267
 4268    // Create a four-line block that replaces three lines of text.
 4269    cx.update_editor(|editor, window, cx| {
 4270        let snapshot = editor.snapshot(window, cx);
 4271        let snapshot = &snapshot.buffer_snapshot;
 4272        let placement = BlockPlacement::Replace(
 4273            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4274        );
 4275        editor.insert_blocks(
 4276            [BlockProperties {
 4277                placement,
 4278                height: Some(4),
 4279                style: BlockStyle::Sticky,
 4280                render: Arc::new(|_| gpui::div().into_any_element()),
 4281                priority: 0,
 4282            }],
 4283            None,
 4284            cx,
 4285        );
 4286    });
 4287
 4288    // Move down so that the cursor touches the block.
 4289    cx.update_editor(|editor, window, cx| {
 4290        editor.move_down(&Default::default(), window, cx);
 4291    });
 4292    cx.assert_editor_state(
 4293        &"
 4294            zero
 4295            «one
 4296            two
 4297            threeˇ»
 4298            four
 4299            five
 4300        "
 4301        .unindent(),
 4302    );
 4303
 4304    // Move down past the block.
 4305    cx.update_editor(|editor, window, cx| {
 4306        editor.move_down(&Default::default(), window, cx);
 4307    });
 4308    cx.assert_editor_state(
 4309        &"
 4310            zero
 4311            one
 4312            two
 4313            three
 4314            ˇfour
 4315            five
 4316        "
 4317        .unindent(),
 4318    );
 4319}
 4320
 4321#[gpui::test]
 4322fn test_transpose(cx: &mut TestAppContext) {
 4323    init_test(cx, |_| {});
 4324
 4325    _ = cx.add_window(|window, cx| {
 4326        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4327        editor.set_style(EditorStyle::default(), window, cx);
 4328        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4329        editor.transpose(&Default::default(), window, cx);
 4330        assert_eq!(editor.text(cx), "bac");
 4331        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4332
 4333        editor.transpose(&Default::default(), window, cx);
 4334        assert_eq!(editor.text(cx), "bca");
 4335        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4336
 4337        editor.transpose(&Default::default(), window, cx);
 4338        assert_eq!(editor.text(cx), "bac");
 4339        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4340
 4341        editor
 4342    });
 4343
 4344    _ = cx.add_window(|window, cx| {
 4345        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4346        editor.set_style(EditorStyle::default(), window, cx);
 4347        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4348        editor.transpose(&Default::default(), window, cx);
 4349        assert_eq!(editor.text(cx), "acb\nde");
 4350        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4351
 4352        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4353        editor.transpose(&Default::default(), window, cx);
 4354        assert_eq!(editor.text(cx), "acbd\ne");
 4355        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4356
 4357        editor.transpose(&Default::default(), window, cx);
 4358        assert_eq!(editor.text(cx), "acbde\n");
 4359        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4360
 4361        editor.transpose(&Default::default(), window, cx);
 4362        assert_eq!(editor.text(cx), "acbd\ne");
 4363        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4364
 4365        editor
 4366    });
 4367
 4368    _ = cx.add_window(|window, cx| {
 4369        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4370        editor.set_style(EditorStyle::default(), window, cx);
 4371        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4372        editor.transpose(&Default::default(), window, cx);
 4373        assert_eq!(editor.text(cx), "bacd\ne");
 4374        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4375
 4376        editor.transpose(&Default::default(), window, cx);
 4377        assert_eq!(editor.text(cx), "bcade\n");
 4378        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4379
 4380        editor.transpose(&Default::default(), window, cx);
 4381        assert_eq!(editor.text(cx), "bcda\ne");
 4382        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4383
 4384        editor.transpose(&Default::default(), window, cx);
 4385        assert_eq!(editor.text(cx), "bcade\n");
 4386        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4387
 4388        editor.transpose(&Default::default(), window, cx);
 4389        assert_eq!(editor.text(cx), "bcaed\n");
 4390        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4391
 4392        editor
 4393    });
 4394
 4395    _ = cx.add_window(|window, cx| {
 4396        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4397        editor.set_style(EditorStyle::default(), window, cx);
 4398        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4399        editor.transpose(&Default::default(), window, cx);
 4400        assert_eq!(editor.text(cx), "🏀🍐✋");
 4401        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4402
 4403        editor.transpose(&Default::default(), window, cx);
 4404        assert_eq!(editor.text(cx), "🏀✋🍐");
 4405        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4406
 4407        editor.transpose(&Default::default(), window, cx);
 4408        assert_eq!(editor.text(cx), "🏀🍐✋");
 4409        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4410
 4411        editor
 4412    });
 4413}
 4414
 4415#[gpui::test]
 4416async fn test_rewrap(cx: &mut TestAppContext) {
 4417    init_test(cx, |settings| {
 4418        settings.languages.extend([
 4419            (
 4420                "Markdown".into(),
 4421                LanguageSettingsContent {
 4422                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4423                    ..Default::default()
 4424                },
 4425            ),
 4426            (
 4427                "Plain Text".into(),
 4428                LanguageSettingsContent {
 4429                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4430                    ..Default::default()
 4431                },
 4432            ),
 4433        ])
 4434    });
 4435
 4436    let mut cx = EditorTestContext::new(cx).await;
 4437
 4438    let language_with_c_comments = Arc::new(Language::new(
 4439        LanguageConfig {
 4440            line_comments: vec!["// ".into()],
 4441            ..LanguageConfig::default()
 4442        },
 4443        None,
 4444    ));
 4445    let language_with_pound_comments = Arc::new(Language::new(
 4446        LanguageConfig {
 4447            line_comments: vec!["# ".into()],
 4448            ..LanguageConfig::default()
 4449        },
 4450        None,
 4451    ));
 4452    let markdown_language = Arc::new(Language::new(
 4453        LanguageConfig {
 4454            name: "Markdown".into(),
 4455            ..LanguageConfig::default()
 4456        },
 4457        None,
 4458    ));
 4459    let language_with_doc_comments = Arc::new(Language::new(
 4460        LanguageConfig {
 4461            line_comments: vec!["// ".into(), "/// ".into()],
 4462            ..LanguageConfig::default()
 4463        },
 4464        Some(tree_sitter_rust::LANGUAGE.into()),
 4465    ));
 4466
 4467    let plaintext_language = Arc::new(Language::new(
 4468        LanguageConfig {
 4469            name: "Plain Text".into(),
 4470            ..LanguageConfig::default()
 4471        },
 4472        None,
 4473    ));
 4474
 4475    assert_rewrap(
 4476        indoc! {"
 4477            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4478        "},
 4479        indoc! {"
 4480            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4481            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4482            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4483            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4484            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4485            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4486            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4487            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4488            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4489            // porttitor id. Aliquam id accumsan eros.
 4490        "},
 4491        language_with_c_comments.clone(),
 4492        &mut cx,
 4493    );
 4494
 4495    // Test that rewrapping works inside of a selection
 4496    assert_rewrap(
 4497        indoc! {"
 4498            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4499        "},
 4500        indoc! {"
 4501            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4502            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4503            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4504            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4505            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4506            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4507            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4508            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4509            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4510            // porttitor id. Aliquam id accumsan eros.ˇ»
 4511        "},
 4512        language_with_c_comments.clone(),
 4513        &mut cx,
 4514    );
 4515
 4516    // Test that cursors that expand to the same region are collapsed.
 4517    assert_rewrap(
 4518        indoc! {"
 4519            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4520            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4521            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4522            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4523        "},
 4524        indoc! {"
 4525            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4526            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4527            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4528            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4529            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4530            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4531            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4532            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4533            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4534            // porttitor id. Aliquam id accumsan eros.
 4535        "},
 4536        language_with_c_comments.clone(),
 4537        &mut cx,
 4538    );
 4539
 4540    // Test that non-contiguous selections are treated separately.
 4541    assert_rewrap(
 4542        indoc! {"
 4543            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4544            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4545            //
 4546            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4547            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        indoc! {"
 4550            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4551            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4552            // auctor, eu lacinia sapien scelerisque.
 4553            //
 4554            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4555            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4556            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4557            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4558            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4559            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4560            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4561        "},
 4562        language_with_c_comments.clone(),
 4563        &mut cx,
 4564    );
 4565
 4566    // Test that different comment prefixes are supported.
 4567    assert_rewrap(
 4568        indoc! {"
 4569            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4570        "},
 4571        indoc! {"
 4572            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4573            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4574            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4575            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4576            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4577            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4578            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4579            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4580            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4581            # accumsan eros.
 4582        "},
 4583        language_with_pound_comments.clone(),
 4584        &mut cx,
 4585    );
 4586
 4587    // Test that rewrapping is ignored outside of comments in most languages.
 4588    assert_rewrap(
 4589        indoc! {"
 4590            /// Adds two numbers.
 4591            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4592            fn add(a: u32, b: u32) -> u32 {
 4593                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4594            }
 4595        "},
 4596        indoc! {"
 4597            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4598            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4599            fn add(a: u32, b: u32) -> u32 {
 4600                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4601            }
 4602        "},
 4603        language_with_doc_comments.clone(),
 4604        &mut cx,
 4605    );
 4606
 4607    // Test that rewrapping works in Markdown and Plain Text languages.
 4608    assert_rewrap(
 4609        indoc! {"
 4610            # Hello
 4611
 4612            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4613        "},
 4614        indoc! {"
 4615            # Hello
 4616
 4617            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4618            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4619            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4620            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4621            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4622            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4623            Integer sit amet scelerisque nisi.
 4624        "},
 4625        markdown_language,
 4626        &mut cx,
 4627    );
 4628
 4629    assert_rewrap(
 4630        indoc! {"
 4631            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4632        "},
 4633        indoc! {"
 4634            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4635            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4636            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4637            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4638            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4639            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4640            Integer sit amet scelerisque nisi.
 4641        "},
 4642        plaintext_language,
 4643        &mut cx,
 4644    );
 4645
 4646    // Test rewrapping unaligned comments in a selection.
 4647    assert_rewrap(
 4648        indoc! {"
 4649            fn foo() {
 4650                if true {
 4651            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4652            // Praesent semper egestas tellus id dignissim.ˇ»
 4653                    do_something();
 4654                } else {
 4655                    //
 4656                }
 4657            }
 4658        "},
 4659        indoc! {"
 4660            fn foo() {
 4661                if true {
 4662            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4663                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4664                    // egestas tellus id dignissim.ˇ»
 4665                    do_something();
 4666                } else {
 4667                    //
 4668                }
 4669            }
 4670        "},
 4671        language_with_doc_comments.clone(),
 4672        &mut cx,
 4673    );
 4674
 4675    assert_rewrap(
 4676        indoc! {"
 4677            fn foo() {
 4678                if true {
 4679            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4680            // Praesent semper egestas tellus id dignissim.»
 4681                    do_something();
 4682                } else {
 4683                    //
 4684                }
 4685
 4686            }
 4687        "},
 4688        indoc! {"
 4689            fn foo() {
 4690                if true {
 4691            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4692                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4693                    // egestas tellus id dignissim.»
 4694                    do_something();
 4695                } else {
 4696                    //
 4697                }
 4698
 4699            }
 4700        "},
 4701        language_with_doc_comments.clone(),
 4702        &mut cx,
 4703    );
 4704
 4705    #[track_caller]
 4706    fn assert_rewrap(
 4707        unwrapped_text: &str,
 4708        wrapped_text: &str,
 4709        language: Arc<Language>,
 4710        cx: &mut EditorTestContext,
 4711    ) {
 4712        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4713        cx.set_state(unwrapped_text);
 4714        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4715        cx.assert_editor_state(wrapped_text);
 4716    }
 4717}
 4718
 4719#[gpui::test]
 4720async fn test_hard_wrap(cx: &mut TestAppContext) {
 4721    init_test(cx, |_| {});
 4722    let mut cx = EditorTestContext::new(cx).await;
 4723
 4724    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4725    cx.update_editor(|editor, _, cx| {
 4726        editor.set_hard_wrap(Some(14), cx);
 4727    });
 4728
 4729    cx.set_state(indoc!(
 4730        "
 4731        one two three ˇ
 4732        "
 4733    ));
 4734    cx.simulate_input("four");
 4735    cx.run_until_parked();
 4736
 4737    cx.assert_editor_state(indoc!(
 4738        "
 4739        one two three
 4740        fourˇ
 4741        "
 4742    ));
 4743
 4744    cx.update_editor(|editor, window, cx| {
 4745        editor.newline(&Default::default(), window, cx);
 4746    });
 4747    cx.run_until_parked();
 4748    cx.assert_editor_state(indoc!(
 4749        "
 4750        one two three
 4751        four
 4752        ˇ
 4753        "
 4754    ));
 4755
 4756    cx.simulate_input("five");
 4757    cx.run_until_parked();
 4758    cx.assert_editor_state(indoc!(
 4759        "
 4760        one two three
 4761        four
 4762        fiveˇ
 4763        "
 4764    ));
 4765
 4766    cx.update_editor(|editor, window, cx| {
 4767        editor.newline(&Default::default(), window, cx);
 4768    });
 4769    cx.run_until_parked();
 4770    cx.simulate_input("# ");
 4771    cx.run_until_parked();
 4772    cx.assert_editor_state(indoc!(
 4773        "
 4774        one two three
 4775        four
 4776        five
 4777        # ˇ
 4778        "
 4779    ));
 4780
 4781    cx.update_editor(|editor, window, cx| {
 4782        editor.newline(&Default::default(), window, cx);
 4783    });
 4784    cx.run_until_parked();
 4785    cx.assert_editor_state(indoc!(
 4786        "
 4787        one two three
 4788        four
 4789        five
 4790        #\x20
 4791 4792        "
 4793    ));
 4794
 4795    cx.simulate_input(" 6");
 4796    cx.run_until_parked();
 4797    cx.assert_editor_state(indoc!(
 4798        "
 4799        one two three
 4800        four
 4801        five
 4802        #
 4803        # 6ˇ
 4804        "
 4805    ));
 4806}
 4807
 4808#[gpui::test]
 4809async fn test_clipboard(cx: &mut TestAppContext) {
 4810    init_test(cx, |_| {});
 4811
 4812    let mut cx = EditorTestContext::new(cx).await;
 4813
 4814    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4815    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4816    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4817
 4818    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4819    cx.set_state("two ˇfour ˇsix ˇ");
 4820    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4821    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4822
 4823    // Paste again but with only two cursors. Since the number of cursors doesn't
 4824    // match the number of slices in the clipboard, the entire clipboard text
 4825    // is pasted at each cursor.
 4826    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4827    cx.update_editor(|e, window, cx| {
 4828        e.handle_input("( ", window, cx);
 4829        e.paste(&Paste, window, cx);
 4830        e.handle_input(") ", window, cx);
 4831    });
 4832    cx.assert_editor_state(
 4833        &([
 4834            "( one✅ ",
 4835            "three ",
 4836            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4837            "three ",
 4838            "five ) ˇ",
 4839        ]
 4840        .join("\n")),
 4841    );
 4842
 4843    // Cut with three selections, one of which is full-line.
 4844    cx.set_state(indoc! {"
 4845        1«2ˇ»3
 4846        4ˇ567
 4847        «8ˇ»9"});
 4848    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4849    cx.assert_editor_state(indoc! {"
 4850        1ˇ3
 4851        ˇ9"});
 4852
 4853    // Paste with three selections, noticing how the copied selection that was full-line
 4854    // gets inserted before the second cursor.
 4855    cx.set_state(indoc! {"
 4856        1ˇ3
 4857 4858        «oˇ»ne"});
 4859    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4860    cx.assert_editor_state(indoc! {"
 4861        12ˇ3
 4862        4567
 4863 4864        8ˇne"});
 4865
 4866    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4867    cx.set_state(indoc! {"
 4868        The quick brown
 4869        fox juˇmps over
 4870        the lazy dog"});
 4871    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4872    assert_eq!(
 4873        cx.read_from_clipboard()
 4874            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4875        Some("fox jumps over\n".to_string())
 4876    );
 4877
 4878    // Paste with three selections, noticing how the copied full-line selection is inserted
 4879    // before the empty selections but replaces the selection that is non-empty.
 4880    cx.set_state(indoc! {"
 4881        Tˇhe quick brown
 4882        «foˇ»x jumps over
 4883        tˇhe lazy dog"});
 4884    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4885    cx.assert_editor_state(indoc! {"
 4886        fox jumps over
 4887        Tˇhe quick brown
 4888        fox jumps over
 4889        ˇx jumps over
 4890        fox jumps over
 4891        tˇhe lazy dog"});
 4892}
 4893
 4894#[gpui::test]
 4895async fn test_copy_trim(cx: &mut TestAppContext) {
 4896    init_test(cx, |_| {});
 4897
 4898    let mut cx = EditorTestContext::new(cx).await;
 4899    cx.set_state(
 4900        r#"            «for selection in selections.iter() {
 4901            let mut start = selection.start;
 4902            let mut end = selection.end;
 4903            let is_entire_line = selection.is_empty();
 4904            if is_entire_line {
 4905                start = Point::new(start.row, 0);ˇ»
 4906                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4907            }
 4908        "#,
 4909    );
 4910    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4911    assert_eq!(
 4912        cx.read_from_clipboard()
 4913            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4914        Some(
 4915            "for selection in selections.iter() {
 4916            let mut start = selection.start;
 4917            let mut end = selection.end;
 4918            let is_entire_line = selection.is_empty();
 4919            if is_entire_line {
 4920                start = Point::new(start.row, 0);"
 4921                .to_string()
 4922        ),
 4923        "Regular copying preserves all indentation selected",
 4924    );
 4925    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4926    assert_eq!(
 4927        cx.read_from_clipboard()
 4928            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4929        Some(
 4930            "for selection in selections.iter() {
 4931let mut start = selection.start;
 4932let mut end = selection.end;
 4933let is_entire_line = selection.is_empty();
 4934if is_entire_line {
 4935    start = Point::new(start.row, 0);"
 4936                .to_string()
 4937        ),
 4938        "Copying with stripping should strip all leading whitespaces"
 4939    );
 4940
 4941    cx.set_state(
 4942        r#"       «     for selection in selections.iter() {
 4943            let mut start = selection.start;
 4944            let mut end = selection.end;
 4945            let is_entire_line = selection.is_empty();
 4946            if is_entire_line {
 4947                start = Point::new(start.row, 0);ˇ»
 4948                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4949            }
 4950        "#,
 4951    );
 4952    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4953    assert_eq!(
 4954        cx.read_from_clipboard()
 4955            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4956        Some(
 4957            "     for selection in selections.iter() {
 4958            let mut start = selection.start;
 4959            let mut end = selection.end;
 4960            let is_entire_line = selection.is_empty();
 4961            if is_entire_line {
 4962                start = Point::new(start.row, 0);"
 4963                .to_string()
 4964        ),
 4965        "Regular copying preserves all indentation selected",
 4966    );
 4967    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4968    assert_eq!(
 4969        cx.read_from_clipboard()
 4970            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4971        Some(
 4972            "for selection in selections.iter() {
 4973let mut start = selection.start;
 4974let mut end = selection.end;
 4975let is_entire_line = selection.is_empty();
 4976if is_entire_line {
 4977    start = Point::new(start.row, 0);"
 4978                .to_string()
 4979        ),
 4980        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 4981    );
 4982
 4983    cx.set_state(
 4984        r#"       «ˇ     for selection in selections.iter() {
 4985            let mut start = selection.start;
 4986            let mut end = selection.end;
 4987            let is_entire_line = selection.is_empty();
 4988            if is_entire_line {
 4989                start = Point::new(start.row, 0);»
 4990                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4991            }
 4992        "#,
 4993    );
 4994    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4995    assert_eq!(
 4996        cx.read_from_clipboard()
 4997            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4998        Some(
 4999            "     for selection in selections.iter() {
 5000            let mut start = selection.start;
 5001            let mut end = selection.end;
 5002            let is_entire_line = selection.is_empty();
 5003            if is_entire_line {
 5004                start = Point::new(start.row, 0);"
 5005                .to_string()
 5006        ),
 5007        "Regular copying for reverse selection works the same",
 5008    );
 5009    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5010    assert_eq!(
 5011        cx.read_from_clipboard()
 5012            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5013        Some(
 5014            "for selection in selections.iter() {
 5015let mut start = selection.start;
 5016let mut end = selection.end;
 5017let is_entire_line = selection.is_empty();
 5018if is_entire_line {
 5019    start = Point::new(start.row, 0);"
 5020                .to_string()
 5021        ),
 5022        "Copying with stripping for reverse selection works the same"
 5023    );
 5024
 5025    cx.set_state(
 5026        r#"            for selection «in selections.iter() {
 5027            let mut start = selection.start;
 5028            let mut end = selection.end;
 5029            let is_entire_line = selection.is_empty();
 5030            if is_entire_line {
 5031                start = Point::new(start.row, 0);ˇ»
 5032                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5033            }
 5034        "#,
 5035    );
 5036    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5037    assert_eq!(
 5038        cx.read_from_clipboard()
 5039            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5040        Some(
 5041            "in selections.iter() {
 5042            let mut start = selection.start;
 5043            let mut end = selection.end;
 5044            let is_entire_line = selection.is_empty();
 5045            if is_entire_line {
 5046                start = Point::new(start.row, 0);"
 5047                .to_string()
 5048        ),
 5049        "When selecting past the indent, the copying works as usual",
 5050    );
 5051    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5052    assert_eq!(
 5053        cx.read_from_clipboard()
 5054            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5055        Some(
 5056            "in selections.iter() {
 5057            let mut start = selection.start;
 5058            let mut end = selection.end;
 5059            let is_entire_line = selection.is_empty();
 5060            if is_entire_line {
 5061                start = Point::new(start.row, 0);"
 5062                .to_string()
 5063        ),
 5064        "When selecting past the indent, nothing is trimmed"
 5065    );
 5066}
 5067
 5068#[gpui::test]
 5069async fn test_paste_multiline(cx: &mut TestAppContext) {
 5070    init_test(cx, |_| {});
 5071
 5072    let mut cx = EditorTestContext::new(cx).await;
 5073    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5074
 5075    // Cut an indented block, without the leading whitespace.
 5076    cx.set_state(indoc! {"
 5077        const a: B = (
 5078            c(),
 5079            «d(
 5080                e,
 5081                f
 5082            )ˇ»
 5083        );
 5084    "});
 5085    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5086    cx.assert_editor_state(indoc! {"
 5087        const a: B = (
 5088            c(),
 5089            ˇ
 5090        );
 5091    "});
 5092
 5093    // Paste it at the same position.
 5094    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5095    cx.assert_editor_state(indoc! {"
 5096        const a: B = (
 5097            c(),
 5098            d(
 5099                e,
 5100                f
 5101 5102        );
 5103    "});
 5104
 5105    // Paste it at a line with a lower indent level.
 5106    cx.set_state(indoc! {"
 5107        ˇ
 5108        const a: B = (
 5109            c(),
 5110        );
 5111    "});
 5112    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5113    cx.assert_editor_state(indoc! {"
 5114        d(
 5115            e,
 5116            f
 5117 5118        const a: B = (
 5119            c(),
 5120        );
 5121    "});
 5122
 5123    // Cut an indented block, with the leading whitespace.
 5124    cx.set_state(indoc! {"
 5125        const a: B = (
 5126            c(),
 5127        «    d(
 5128                e,
 5129                f
 5130            )
 5131        ˇ»);
 5132    "});
 5133    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5134    cx.assert_editor_state(indoc! {"
 5135        const a: B = (
 5136            c(),
 5137        ˇ);
 5138    "});
 5139
 5140    // Paste it at the same position.
 5141    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5142    cx.assert_editor_state(indoc! {"
 5143        const a: B = (
 5144            c(),
 5145            d(
 5146                e,
 5147                f
 5148            )
 5149        ˇ);
 5150    "});
 5151
 5152    // Paste it at a line with a higher indent level.
 5153    cx.set_state(indoc! {"
 5154        const a: B = (
 5155            c(),
 5156            d(
 5157                e,
 5158 5159            )
 5160        );
 5161    "});
 5162    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5163    cx.assert_editor_state(indoc! {"
 5164        const a: B = (
 5165            c(),
 5166            d(
 5167                e,
 5168                f    d(
 5169                    e,
 5170                    f
 5171                )
 5172        ˇ
 5173            )
 5174        );
 5175    "});
 5176
 5177    // Copy an indented block, starting mid-line
 5178    cx.set_state(indoc! {"
 5179        const a: B = (
 5180            c(),
 5181            somethin«g(
 5182                e,
 5183                f
 5184            )ˇ»
 5185        );
 5186    "});
 5187    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5188
 5189    // Paste it on a line with a lower indent level
 5190    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5191    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5192    cx.assert_editor_state(indoc! {"
 5193        const a: B = (
 5194            c(),
 5195            something(
 5196                e,
 5197                f
 5198            )
 5199        );
 5200        g(
 5201            e,
 5202            f
 5203"});
 5204}
 5205
 5206#[gpui::test]
 5207async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5208    init_test(cx, |_| {});
 5209
 5210    cx.write_to_clipboard(ClipboardItem::new_string(
 5211        "    d(\n        e\n    );\n".into(),
 5212    ));
 5213
 5214    let mut cx = EditorTestContext::new(cx).await;
 5215    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5216
 5217    cx.set_state(indoc! {"
 5218        fn a() {
 5219            b();
 5220            if c() {
 5221                ˇ
 5222            }
 5223        }
 5224    "});
 5225
 5226    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5227    cx.assert_editor_state(indoc! {"
 5228        fn a() {
 5229            b();
 5230            if c() {
 5231                d(
 5232                    e
 5233                );
 5234        ˇ
 5235            }
 5236        }
 5237    "});
 5238
 5239    cx.set_state(indoc! {"
 5240        fn a() {
 5241            b();
 5242            ˇ
 5243        }
 5244    "});
 5245
 5246    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5247    cx.assert_editor_state(indoc! {"
 5248        fn a() {
 5249            b();
 5250            d(
 5251                e
 5252            );
 5253        ˇ
 5254        }
 5255    "});
 5256}
 5257
 5258#[gpui::test]
 5259fn test_select_all(cx: &mut TestAppContext) {
 5260    init_test(cx, |_| {});
 5261
 5262    let editor = cx.add_window(|window, cx| {
 5263        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5264        build_editor(buffer, window, cx)
 5265    });
 5266    _ = editor.update(cx, |editor, window, cx| {
 5267        editor.select_all(&SelectAll, window, cx);
 5268        assert_eq!(
 5269            editor.selections.display_ranges(cx),
 5270            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5271        );
 5272    });
 5273}
 5274
 5275#[gpui::test]
 5276fn test_select_line(cx: &mut TestAppContext) {
 5277    init_test(cx, |_| {});
 5278
 5279    let editor = cx.add_window(|window, cx| {
 5280        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5281        build_editor(buffer, window, cx)
 5282    });
 5283    _ = editor.update(cx, |editor, window, cx| {
 5284        editor.change_selections(None, window, cx, |s| {
 5285            s.select_display_ranges([
 5286                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5287                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5288                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5289                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5290            ])
 5291        });
 5292        editor.select_line(&SelectLine, window, cx);
 5293        assert_eq!(
 5294            editor.selections.display_ranges(cx),
 5295            vec![
 5296                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5297                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5298            ]
 5299        );
 5300    });
 5301
 5302    _ = editor.update(cx, |editor, window, cx| {
 5303        editor.select_line(&SelectLine, window, cx);
 5304        assert_eq!(
 5305            editor.selections.display_ranges(cx),
 5306            vec![
 5307                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5308                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5309            ]
 5310        );
 5311    });
 5312
 5313    _ = editor.update(cx, |editor, window, cx| {
 5314        editor.select_line(&SelectLine, window, cx);
 5315        assert_eq!(
 5316            editor.selections.display_ranges(cx),
 5317            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5318        );
 5319    });
 5320}
 5321
 5322#[gpui::test]
 5323async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5324    init_test(cx, |_| {});
 5325    let mut cx = EditorTestContext::new(cx).await;
 5326
 5327    #[track_caller]
 5328    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5329        cx.set_state(initial_state);
 5330        cx.update_editor(|e, window, cx| {
 5331            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5332        });
 5333        cx.assert_editor_state(expected_state);
 5334    }
 5335
 5336    // Selection starts and ends at the middle of lines, left-to-right
 5337    test(
 5338        &mut cx,
 5339        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5340        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5341    );
 5342    // Same thing, right-to-left
 5343    test(
 5344        &mut cx,
 5345        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5346        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5347    );
 5348
 5349    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5350    test(
 5351        &mut cx,
 5352        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5353        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5354    );
 5355    // Same thing, right-to-left
 5356    test(
 5357        &mut cx,
 5358        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5359        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5360    );
 5361
 5362    // Whole buffer, left-to-right, last line ends with newline
 5363    test(
 5364        &mut cx,
 5365        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5366        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5367    );
 5368    // Same thing, right-to-left
 5369    test(
 5370        &mut cx,
 5371        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5372        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5373    );
 5374
 5375    // Starts at the end of a line, ends at the start of another
 5376    test(
 5377        &mut cx,
 5378        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5379        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5380    );
 5381}
 5382
 5383#[gpui::test]
 5384async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5385    init_test(cx, |_| {});
 5386
 5387    let editor = cx.add_window(|window, cx| {
 5388        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5389        build_editor(buffer, window, cx)
 5390    });
 5391
 5392    // setup
 5393    _ = editor.update(cx, |editor, window, cx| {
 5394        editor.fold_creases(
 5395            vec![
 5396                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5397                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5398                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5399            ],
 5400            true,
 5401            window,
 5402            cx,
 5403        );
 5404        assert_eq!(
 5405            editor.display_text(cx),
 5406            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5407        );
 5408    });
 5409
 5410    _ = editor.update(cx, |editor, window, cx| {
 5411        editor.change_selections(None, window, cx, |s| {
 5412            s.select_display_ranges([
 5413                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5414                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5415                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5416                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5417            ])
 5418        });
 5419        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5420        assert_eq!(
 5421            editor.display_text(cx),
 5422            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5423        );
 5424    });
 5425    EditorTestContext::for_editor(editor, cx)
 5426        .await
 5427        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5428
 5429    _ = editor.update(cx, |editor, window, cx| {
 5430        editor.change_selections(None, window, cx, |s| {
 5431            s.select_display_ranges([
 5432                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5433            ])
 5434        });
 5435        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5436        assert_eq!(
 5437            editor.display_text(cx),
 5438            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5439        );
 5440        assert_eq!(
 5441            editor.selections.display_ranges(cx),
 5442            [
 5443                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5444                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5445                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5446                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5447                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5448                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5449                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5450            ]
 5451        );
 5452    });
 5453    EditorTestContext::for_editor(editor, cx)
 5454        .await
 5455        .assert_editor_state(
 5456            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5457        );
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let mut cx = EditorTestContext::new(cx).await;
 5465
 5466    cx.set_state(indoc!(
 5467        r#"abc
 5468           defˇghi
 5469
 5470           jk
 5471           nlmo
 5472           "#
 5473    ));
 5474
 5475    cx.update_editor(|editor, window, cx| {
 5476        editor.add_selection_above(&Default::default(), window, cx);
 5477    });
 5478
 5479    cx.assert_editor_state(indoc!(
 5480        r#"abcˇ
 5481           defˇghi
 5482
 5483           jk
 5484           nlmo
 5485           "#
 5486    ));
 5487
 5488    cx.update_editor(|editor, window, cx| {
 5489        editor.add_selection_above(&Default::default(), window, cx);
 5490    });
 5491
 5492    cx.assert_editor_state(indoc!(
 5493        r#"abcˇ
 5494            defˇghi
 5495
 5496            jk
 5497            nlmo
 5498            "#
 5499    ));
 5500
 5501    cx.update_editor(|editor, window, cx| {
 5502        editor.add_selection_below(&Default::default(), window, cx);
 5503    });
 5504
 5505    cx.assert_editor_state(indoc!(
 5506        r#"abc
 5507           defˇghi
 5508
 5509           jk
 5510           nlmo
 5511           "#
 5512    ));
 5513
 5514    cx.update_editor(|editor, window, cx| {
 5515        editor.undo_selection(&Default::default(), window, cx);
 5516    });
 5517
 5518    cx.assert_editor_state(indoc!(
 5519        r#"abcˇ
 5520           defˇghi
 5521
 5522           jk
 5523           nlmo
 5524           "#
 5525    ));
 5526
 5527    cx.update_editor(|editor, window, cx| {
 5528        editor.redo_selection(&Default::default(), window, cx);
 5529    });
 5530
 5531    cx.assert_editor_state(indoc!(
 5532        r#"abc
 5533           defˇghi
 5534
 5535           jk
 5536           nlmo
 5537           "#
 5538    ));
 5539
 5540    cx.update_editor(|editor, window, cx| {
 5541        editor.add_selection_below(&Default::default(), window, cx);
 5542    });
 5543
 5544    cx.assert_editor_state(indoc!(
 5545        r#"abc
 5546           defˇghi
 5547
 5548           jk
 5549           nlmˇo
 5550           "#
 5551    ));
 5552
 5553    cx.update_editor(|editor, window, cx| {
 5554        editor.add_selection_below(&Default::default(), window, cx);
 5555    });
 5556
 5557    cx.assert_editor_state(indoc!(
 5558        r#"abc
 5559           defˇghi
 5560
 5561           jk
 5562           nlmˇo
 5563           "#
 5564    ));
 5565
 5566    // change selections
 5567    cx.set_state(indoc!(
 5568        r#"abc
 5569           def«ˇg»hi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_below(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abc
 5582           def«ˇg»hi
 5583
 5584           jk
 5585           nlm«ˇo»
 5586           "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           def«ˇg»hi
 5596
 5597           jk
 5598           nlm«ˇo»
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.add_selection_above(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abc
 5608           def«ˇg»hi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.add_selection_above(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           def«ˇg»hi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    // Change selections again
 5629    cx.set_state(indoc!(
 5630        r#"a«bc
 5631           defgˇ»hi
 5632
 5633           jk
 5634           nlmo
 5635           "#
 5636    ));
 5637
 5638    cx.update_editor(|editor, window, cx| {
 5639        editor.add_selection_below(&Default::default(), window, cx);
 5640    });
 5641
 5642    cx.assert_editor_state(indoc!(
 5643        r#"a«bcˇ»
 5644           d«efgˇ»hi
 5645
 5646           j«kˇ»
 5647           nlmo
 5648           "#
 5649    ));
 5650
 5651    cx.update_editor(|editor, window, cx| {
 5652        editor.add_selection_below(&Default::default(), window, cx);
 5653    });
 5654    cx.assert_editor_state(indoc!(
 5655        r#"a«bcˇ»
 5656           d«efgˇ»hi
 5657
 5658           j«kˇ»
 5659           n«lmoˇ»
 5660           "#
 5661    ));
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_above(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"a«bcˇ»
 5668           d«efgˇ»hi
 5669
 5670           j«kˇ»
 5671           nlmo
 5672           "#
 5673    ));
 5674
 5675    // Change selections again
 5676    cx.set_state(indoc!(
 5677        r#"abc
 5678           d«ˇefghi
 5679
 5680           jk
 5681           nlm»o
 5682           "#
 5683    ));
 5684
 5685    cx.update_editor(|editor, window, cx| {
 5686        editor.add_selection_above(&Default::default(), window, cx);
 5687    });
 5688
 5689    cx.assert_editor_state(indoc!(
 5690        r#"a«ˇbc»
 5691           d«ˇef»ghi
 5692
 5693           j«ˇk»
 5694           n«ˇlm»o
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"abc
 5704           d«ˇef»ghi
 5705
 5706           j«ˇk»
 5707           n«ˇlm»o
 5708           "#
 5709    ));
 5710}
 5711
 5712#[gpui::test]
 5713async fn test_select_next(cx: &mut TestAppContext) {
 5714    init_test(cx, |_| {});
 5715
 5716    let mut cx = EditorTestContext::new(cx).await;
 5717    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5718
 5719    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5720        .unwrap();
 5721    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5722
 5723    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5724        .unwrap();
 5725    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5726
 5727    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5728    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5729
 5730    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5731    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5732
 5733    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5734        .unwrap();
 5735    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5736
 5737    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5738        .unwrap();
 5739    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5740}
 5741
 5742#[gpui::test]
 5743async fn test_select_all_matches(cx: &mut TestAppContext) {
 5744    init_test(cx, |_| {});
 5745
 5746    let mut cx = EditorTestContext::new(cx).await;
 5747
 5748    // Test caret-only selections
 5749    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5750    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5751        .unwrap();
 5752    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5753
 5754    // Test left-to-right selections
 5755    cx.set_state("abc\n«abcˇ»\nabc");
 5756    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5757        .unwrap();
 5758    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5759
 5760    // Test right-to-left selections
 5761    cx.set_state("abc\n«ˇabc»\nabc");
 5762    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5763        .unwrap();
 5764    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5765
 5766    // Test selecting whitespace with caret selection
 5767    cx.set_state("abc\nˇ   abc\nabc");
 5768    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5769        .unwrap();
 5770    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5771
 5772    // Test selecting whitespace with left-to-right selection
 5773    cx.set_state("abc\n«ˇ  »abc\nabc");
 5774    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5775        .unwrap();
 5776    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5777
 5778    // Test no matches with right-to-left selection
 5779    cx.set_state("abc\n«  ˇ»abc\nabc");
 5780    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5781        .unwrap();
 5782    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5783}
 5784
 5785#[gpui::test]
 5786async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5787    init_test(cx, |_| {});
 5788
 5789    let mut cx = EditorTestContext::new(cx).await;
 5790    cx.set_state(
 5791        r#"let foo = 2;
 5792lˇet foo = 2;
 5793let fooˇ = 2;
 5794let foo = 2;
 5795let foo = ˇ2;"#,
 5796    );
 5797
 5798    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5799        .unwrap();
 5800    cx.assert_editor_state(
 5801        r#"let foo = 2;
 5802«letˇ» foo = 2;
 5803let «fooˇ» = 2;
 5804let foo = 2;
 5805let foo = «2ˇ»;"#,
 5806    );
 5807
 5808    // noop for multiple selections with different contents
 5809    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5810        .unwrap();
 5811    cx.assert_editor_state(
 5812        r#"let foo = 2;
 5813«letˇ» foo = 2;
 5814let «fooˇ» = 2;
 5815let foo = 2;
 5816let foo = «2ˇ»;"#,
 5817    );
 5818}
 5819
 5820#[gpui::test]
 5821async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5822    init_test(cx, |_| {});
 5823
 5824    let mut cx =
 5825        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5826
 5827    cx.assert_editor_state(indoc! {"
 5828        ˇbbb
 5829        ccc
 5830
 5831        bbb
 5832        ccc
 5833        "});
 5834    cx.dispatch_action(SelectPrevious::default());
 5835    cx.assert_editor_state(indoc! {"
 5836                «bbbˇ»
 5837                ccc
 5838
 5839                bbb
 5840                ccc
 5841                "});
 5842    cx.dispatch_action(SelectPrevious::default());
 5843    cx.assert_editor_state(indoc! {"
 5844                «bbbˇ»
 5845                ccc
 5846
 5847                «bbbˇ»
 5848                ccc
 5849                "});
 5850}
 5851
 5852#[gpui::test]
 5853async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5854    init_test(cx, |_| {});
 5855
 5856    let mut cx = EditorTestContext::new(cx).await;
 5857    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5858
 5859    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5860        .unwrap();
 5861    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5862
 5863    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5866
 5867    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5868    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5869
 5870    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5871    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5872
 5873    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5874        .unwrap();
 5875    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5876
 5877    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5878        .unwrap();
 5879    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5880
 5881    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5882        .unwrap();
 5883    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5884}
 5885
 5886#[gpui::test]
 5887async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5888    init_test(cx, |_| {});
 5889
 5890    let mut cx = EditorTestContext::new(cx).await;
 5891    cx.set_state("");
 5892
 5893    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5894        .unwrap();
 5895    cx.assert_editor_state("«aˇ»");
 5896    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5897        .unwrap();
 5898    cx.assert_editor_state("«aˇ»");
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let mut cx = EditorTestContext::new(cx).await;
 5906    cx.set_state(
 5907        r#"let foo = 2;
 5908lˇet foo = 2;
 5909let fooˇ = 2;
 5910let foo = 2;
 5911let foo = ˇ2;"#,
 5912    );
 5913
 5914    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5915        .unwrap();
 5916    cx.assert_editor_state(
 5917        r#"let foo = 2;
 5918«letˇ» foo = 2;
 5919let «fooˇ» = 2;
 5920let foo = 2;
 5921let foo = «2ˇ»;"#,
 5922    );
 5923
 5924    // noop for multiple selections with different contents
 5925    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5926        .unwrap();
 5927    cx.assert_editor_state(
 5928        r#"let foo = 2;
 5929«letˇ» foo = 2;
 5930let «fooˇ» = 2;
 5931let foo = 2;
 5932let foo = «2ˇ»;"#,
 5933    );
 5934}
 5935
 5936#[gpui::test]
 5937async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5938    init_test(cx, |_| {});
 5939
 5940    let mut cx = EditorTestContext::new(cx).await;
 5941    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5942
 5943    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5944        .unwrap();
 5945    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5946
 5947    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5948        .unwrap();
 5949    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5950
 5951    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5952    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5953
 5954    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5955    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5956
 5957    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5958        .unwrap();
 5959    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5960
 5961    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5962        .unwrap();
 5963    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5964}
 5965
 5966#[gpui::test]
 5967async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5968    init_test(cx, |_| {});
 5969
 5970    let language = Arc::new(Language::new(
 5971        LanguageConfig::default(),
 5972        Some(tree_sitter_rust::LANGUAGE.into()),
 5973    ));
 5974
 5975    let text = r#"
 5976        use mod1::mod2::{mod3, mod4};
 5977
 5978        fn fn_1(param1: bool, param2: &str) {
 5979            let var1 = "text";
 5980        }
 5981    "#
 5982    .unindent();
 5983
 5984    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5985    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5986    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5987
 5988    editor
 5989        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5990        .await;
 5991
 5992    editor.update_in(cx, |editor, window, cx| {
 5993        editor.change_selections(None, window, cx, |s| {
 5994            s.select_display_ranges([
 5995                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5996                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5997                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5998            ]);
 5999        });
 6000        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6001    });
 6002    editor.update(cx, |editor, cx| {
 6003        assert_text_with_selections(
 6004            editor,
 6005            indoc! {r#"
 6006                use mod1::mod2::{mod3, «mod4ˇ»};
 6007
 6008                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6009                    let var1 = "«ˇtext»";
 6010                }
 6011            "#},
 6012            cx,
 6013        );
 6014    });
 6015
 6016    editor.update_in(cx, |editor, window, cx| {
 6017        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6018    });
 6019    editor.update(cx, |editor, cx| {
 6020        assert_text_with_selections(
 6021            editor,
 6022            indoc! {r#"
 6023                use mod1::mod2::«{mod3, mod4}ˇ»;
 6024
 6025                «ˇfn fn_1(param1: bool, param2: &str) {
 6026                    let var1 = "text";
 6027 6028            "#},
 6029            cx,
 6030        );
 6031    });
 6032
 6033    editor.update_in(cx, |editor, window, cx| {
 6034        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6035    });
 6036    assert_eq!(
 6037        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6038        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6039    );
 6040
 6041    // Trying to expand the selected syntax node one more time has no effect.
 6042    editor.update_in(cx, |editor, window, cx| {
 6043        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6044    });
 6045    assert_eq!(
 6046        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6047        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6048    );
 6049
 6050    editor.update_in(cx, |editor, window, cx| {
 6051        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6052    });
 6053    editor.update(cx, |editor, cx| {
 6054        assert_text_with_selections(
 6055            editor,
 6056            indoc! {r#"
 6057                use mod1::mod2::«{mod3, mod4}ˇ»;
 6058
 6059                «ˇfn fn_1(param1: bool, param2: &str) {
 6060                    let var1 = "text";
 6061 6062            "#},
 6063            cx,
 6064        );
 6065    });
 6066
 6067    editor.update_in(cx, |editor, window, cx| {
 6068        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6069    });
 6070    editor.update(cx, |editor, cx| {
 6071        assert_text_with_selections(
 6072            editor,
 6073            indoc! {r#"
 6074                use mod1::mod2::{mod3, «mod4ˇ»};
 6075
 6076                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6077                    let var1 = "«ˇtext»";
 6078                }
 6079            "#},
 6080            cx,
 6081        );
 6082    });
 6083
 6084    editor.update_in(cx, |editor, window, cx| {
 6085        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6086    });
 6087    editor.update(cx, |editor, cx| {
 6088        assert_text_with_selections(
 6089            editor,
 6090            indoc! {r#"
 6091                use mod1::mod2::{mod3, mo«ˇ»d4};
 6092
 6093                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6094                    let var1 = "te«ˇ»xt";
 6095                }
 6096            "#},
 6097            cx,
 6098        );
 6099    });
 6100
 6101    // Trying to shrink the selected syntax node one more time has no effect.
 6102    editor.update_in(cx, |editor, window, cx| {
 6103        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6104    });
 6105    editor.update_in(cx, |editor, _, cx| {
 6106        assert_text_with_selections(
 6107            editor,
 6108            indoc! {r#"
 6109                use mod1::mod2::{mod3, mo«ˇ»d4};
 6110
 6111                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6112                    let var1 = "te«ˇ»xt";
 6113                }
 6114            "#},
 6115            cx,
 6116        );
 6117    });
 6118
 6119    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6120    // a fold.
 6121    editor.update_in(cx, |editor, window, cx| {
 6122        editor.fold_creases(
 6123            vec![
 6124                Crease::simple(
 6125                    Point::new(0, 21)..Point::new(0, 24),
 6126                    FoldPlaceholder::test(),
 6127                ),
 6128                Crease::simple(
 6129                    Point::new(3, 20)..Point::new(3, 22),
 6130                    FoldPlaceholder::test(),
 6131                ),
 6132            ],
 6133            true,
 6134            window,
 6135            cx,
 6136        );
 6137        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6138    });
 6139    editor.update(cx, |editor, cx| {
 6140        assert_text_with_selections(
 6141            editor,
 6142            indoc! {r#"
 6143                use mod1::mod2::«{mod3, mod4}ˇ»;
 6144
 6145                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6146                    «ˇlet var1 = "text";»
 6147                }
 6148            "#},
 6149            cx,
 6150        );
 6151    });
 6152}
 6153
 6154#[gpui::test]
 6155async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6156    init_test(cx, |_| {});
 6157
 6158    let base_text = r#"
 6159        impl A {
 6160            // this is an uncommitted comment
 6161
 6162            fn b() {
 6163                c();
 6164            }
 6165
 6166            // this is another uncommitted comment
 6167
 6168            fn d() {
 6169                // e
 6170                // f
 6171            }
 6172        }
 6173
 6174        fn g() {
 6175            // h
 6176        }
 6177    "#
 6178    .unindent();
 6179
 6180    let text = r#"
 6181        ˇimpl A {
 6182
 6183            fn b() {
 6184                c();
 6185            }
 6186
 6187            fn d() {
 6188                // e
 6189                // f
 6190            }
 6191        }
 6192
 6193        fn g() {
 6194            // h
 6195        }
 6196    "#
 6197    .unindent();
 6198
 6199    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6200    cx.set_state(&text);
 6201    cx.set_head_text(&base_text);
 6202    cx.update_editor(|editor, window, cx| {
 6203        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6204    });
 6205
 6206    cx.assert_state_with_diff(
 6207        "
 6208        ˇimpl A {
 6209      -     // this is an uncommitted comment
 6210
 6211            fn b() {
 6212                c();
 6213            }
 6214
 6215      -     // this is another uncommitted comment
 6216      -
 6217            fn d() {
 6218                // e
 6219                // f
 6220            }
 6221        }
 6222
 6223        fn g() {
 6224            // h
 6225        }
 6226    "
 6227        .unindent(),
 6228    );
 6229
 6230    let expected_display_text = "
 6231        impl A {
 6232            // this is an uncommitted comment
 6233
 6234            fn b() {
 6235 6236            }
 6237
 6238            // this is another uncommitted comment
 6239
 6240            fn d() {
 6241 6242            }
 6243        }
 6244
 6245        fn g() {
 6246 6247        }
 6248        "
 6249    .unindent();
 6250
 6251    cx.update_editor(|editor, window, cx| {
 6252        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6253        assert_eq!(editor.display_text(cx), expected_display_text);
 6254    });
 6255}
 6256
 6257#[gpui::test]
 6258async fn test_autoindent(cx: &mut TestAppContext) {
 6259    init_test(cx, |_| {});
 6260
 6261    let language = Arc::new(
 6262        Language::new(
 6263            LanguageConfig {
 6264                brackets: BracketPairConfig {
 6265                    pairs: vec![
 6266                        BracketPair {
 6267                            start: "{".to_string(),
 6268                            end: "}".to_string(),
 6269                            close: false,
 6270                            surround: false,
 6271                            newline: true,
 6272                        },
 6273                        BracketPair {
 6274                            start: "(".to_string(),
 6275                            end: ")".to_string(),
 6276                            close: false,
 6277                            surround: false,
 6278                            newline: true,
 6279                        },
 6280                    ],
 6281                    ..Default::default()
 6282                },
 6283                ..Default::default()
 6284            },
 6285            Some(tree_sitter_rust::LANGUAGE.into()),
 6286        )
 6287        .with_indents_query(
 6288            r#"
 6289                (_ "(" ")" @end) @indent
 6290                (_ "{" "}" @end) @indent
 6291            "#,
 6292        )
 6293        .unwrap(),
 6294    );
 6295
 6296    let text = "fn a() {}";
 6297
 6298    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6299    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6300    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6301    editor
 6302        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6303        .await;
 6304
 6305    editor.update_in(cx, |editor, window, cx| {
 6306        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6307        editor.newline(&Newline, window, cx);
 6308        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6309        assert_eq!(
 6310            editor.selections.ranges(cx),
 6311            &[
 6312                Point::new(1, 4)..Point::new(1, 4),
 6313                Point::new(3, 4)..Point::new(3, 4),
 6314                Point::new(5, 0)..Point::new(5, 0)
 6315            ]
 6316        );
 6317    });
 6318}
 6319
 6320#[gpui::test]
 6321async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6322    init_test(cx, |_| {});
 6323
 6324    {
 6325        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6326        cx.set_state(indoc! {"
 6327            impl A {
 6328
 6329                fn b() {}
 6330
 6331            «fn c() {
 6332
 6333            }ˇ»
 6334            }
 6335        "});
 6336
 6337        cx.update_editor(|editor, window, cx| {
 6338            editor.autoindent(&Default::default(), window, cx);
 6339        });
 6340
 6341        cx.assert_editor_state(indoc! {"
 6342            impl A {
 6343
 6344                fn b() {}
 6345
 6346                «fn c() {
 6347
 6348                }ˇ»
 6349            }
 6350        "});
 6351    }
 6352
 6353    {
 6354        let mut cx = EditorTestContext::new_multibuffer(
 6355            cx,
 6356            [indoc! { "
 6357                impl A {
 6358                «
 6359                // a
 6360                fn b(){}
 6361                »
 6362                «
 6363                    }
 6364                    fn c(){}
 6365                »
 6366            "}],
 6367        );
 6368
 6369        let buffer = cx.update_editor(|editor, _, cx| {
 6370            let buffer = editor.buffer().update(cx, |buffer, _| {
 6371                buffer.all_buffers().iter().next().unwrap().clone()
 6372            });
 6373            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6374            buffer
 6375        });
 6376
 6377        cx.run_until_parked();
 6378        cx.update_editor(|editor, window, cx| {
 6379            editor.select_all(&Default::default(), window, cx);
 6380            editor.autoindent(&Default::default(), window, cx)
 6381        });
 6382        cx.run_until_parked();
 6383
 6384        cx.update(|_, cx| {
 6385            assert_eq!(
 6386                buffer.read(cx).text(),
 6387                indoc! { "
 6388                    impl A {
 6389
 6390                        // a
 6391                        fn b(){}
 6392
 6393
 6394                    }
 6395                    fn c(){}
 6396
 6397                " }
 6398            )
 6399        });
 6400    }
 6401}
 6402
 6403#[gpui::test]
 6404async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6405    init_test(cx, |_| {});
 6406
 6407    let mut cx = EditorTestContext::new(cx).await;
 6408
 6409    let language = Arc::new(Language::new(
 6410        LanguageConfig {
 6411            brackets: BracketPairConfig {
 6412                pairs: vec![
 6413                    BracketPair {
 6414                        start: "{".to_string(),
 6415                        end: "}".to_string(),
 6416                        close: true,
 6417                        surround: true,
 6418                        newline: true,
 6419                    },
 6420                    BracketPair {
 6421                        start: "(".to_string(),
 6422                        end: ")".to_string(),
 6423                        close: true,
 6424                        surround: true,
 6425                        newline: true,
 6426                    },
 6427                    BracketPair {
 6428                        start: "/*".to_string(),
 6429                        end: " */".to_string(),
 6430                        close: true,
 6431                        surround: true,
 6432                        newline: true,
 6433                    },
 6434                    BracketPair {
 6435                        start: "[".to_string(),
 6436                        end: "]".to_string(),
 6437                        close: false,
 6438                        surround: false,
 6439                        newline: true,
 6440                    },
 6441                    BracketPair {
 6442                        start: "\"".to_string(),
 6443                        end: "\"".to_string(),
 6444                        close: true,
 6445                        surround: true,
 6446                        newline: false,
 6447                    },
 6448                    BracketPair {
 6449                        start: "<".to_string(),
 6450                        end: ">".to_string(),
 6451                        close: false,
 6452                        surround: true,
 6453                        newline: true,
 6454                    },
 6455                ],
 6456                ..Default::default()
 6457            },
 6458            autoclose_before: "})]".to_string(),
 6459            ..Default::default()
 6460        },
 6461        Some(tree_sitter_rust::LANGUAGE.into()),
 6462    ));
 6463
 6464    cx.language_registry().add(language.clone());
 6465    cx.update_buffer(|buffer, cx| {
 6466        buffer.set_language(Some(language), cx);
 6467    });
 6468
 6469    cx.set_state(
 6470        &r#"
 6471            🏀ˇ
 6472            εˇ
 6473            ❤️ˇ
 6474        "#
 6475        .unindent(),
 6476    );
 6477
 6478    // autoclose multiple nested brackets at multiple cursors
 6479    cx.update_editor(|editor, window, cx| {
 6480        editor.handle_input("{", window, cx);
 6481        editor.handle_input("{", window, cx);
 6482        editor.handle_input("{", window, cx);
 6483    });
 6484    cx.assert_editor_state(
 6485        &"
 6486            🏀{{{ˇ}}}
 6487            ε{{{ˇ}}}
 6488            ❤️{{{ˇ}}}
 6489        "
 6490        .unindent(),
 6491    );
 6492
 6493    // insert a different closing bracket
 6494    cx.update_editor(|editor, window, cx| {
 6495        editor.handle_input(")", window, cx);
 6496    });
 6497    cx.assert_editor_state(
 6498        &"
 6499            🏀{{{)ˇ}}}
 6500            ε{{{)ˇ}}}
 6501            ❤️{{{)ˇ}}}
 6502        "
 6503        .unindent(),
 6504    );
 6505
 6506    // skip over the auto-closed brackets when typing a closing bracket
 6507    cx.update_editor(|editor, window, cx| {
 6508        editor.move_right(&MoveRight, window, cx);
 6509        editor.handle_input("}", window, cx);
 6510        editor.handle_input("}", window, cx);
 6511        editor.handle_input("}", window, cx);
 6512    });
 6513    cx.assert_editor_state(
 6514        &"
 6515            🏀{{{)}}}}ˇ
 6516            ε{{{)}}}}ˇ
 6517            ❤️{{{)}}}}ˇ
 6518        "
 6519        .unindent(),
 6520    );
 6521
 6522    // autoclose multi-character pairs
 6523    cx.set_state(
 6524        &"
 6525            ˇ
 6526            ˇ
 6527        "
 6528        .unindent(),
 6529    );
 6530    cx.update_editor(|editor, window, cx| {
 6531        editor.handle_input("/", window, cx);
 6532        editor.handle_input("*", window, cx);
 6533    });
 6534    cx.assert_editor_state(
 6535        &"
 6536            /*ˇ */
 6537            /*ˇ */
 6538        "
 6539        .unindent(),
 6540    );
 6541
 6542    // one cursor autocloses a multi-character pair, one cursor
 6543    // does not autoclose.
 6544    cx.set_state(
 6545        &"
 6546 6547            ˇ
 6548        "
 6549        .unindent(),
 6550    );
 6551    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6552    cx.assert_editor_state(
 6553        &"
 6554            /*ˇ */
 6555 6556        "
 6557        .unindent(),
 6558    );
 6559
 6560    // Don't autoclose if the next character isn't whitespace and isn't
 6561    // listed in the language's "autoclose_before" section.
 6562    cx.set_state("ˇa b");
 6563    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6564    cx.assert_editor_state("{ˇa b");
 6565
 6566    // Don't autoclose if `close` is false for the bracket pair
 6567    cx.set_state("ˇ");
 6568    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6569    cx.assert_editor_state("");
 6570
 6571    // Surround with brackets if text is selected
 6572    cx.set_state("«aˇ» b");
 6573    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6574    cx.assert_editor_state("{«aˇ»} b");
 6575
 6576    // Autoclose when not immediately after a word character
 6577    cx.set_state("a ˇ");
 6578    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6579    cx.assert_editor_state("a \"ˇ\"");
 6580
 6581    // Autoclose pair where the start and end characters are the same
 6582    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6583    cx.assert_editor_state("a \"\"ˇ");
 6584
 6585    // Don't autoclose when immediately after a word character
 6586    cx.set_state("");
 6587    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6588    cx.assert_editor_state("a\"ˇ");
 6589
 6590    // Do autoclose when after a non-word character
 6591    cx.set_state("");
 6592    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6593    cx.assert_editor_state("{\"ˇ\"");
 6594
 6595    // Non identical pairs autoclose regardless of preceding character
 6596    cx.set_state("");
 6597    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6598    cx.assert_editor_state("a{ˇ}");
 6599
 6600    // Don't autoclose pair if autoclose is disabled
 6601    cx.set_state("ˇ");
 6602    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6603    cx.assert_editor_state("");
 6604
 6605    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6606    cx.set_state("«aˇ» b");
 6607    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6608    cx.assert_editor_state("<«aˇ»> b");
 6609}
 6610
 6611#[gpui::test]
 6612async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6613    init_test(cx, |settings| {
 6614        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6615    });
 6616
 6617    let mut cx = EditorTestContext::new(cx).await;
 6618
 6619    let language = Arc::new(Language::new(
 6620        LanguageConfig {
 6621            brackets: BracketPairConfig {
 6622                pairs: vec![
 6623                    BracketPair {
 6624                        start: "{".to_string(),
 6625                        end: "}".to_string(),
 6626                        close: true,
 6627                        surround: true,
 6628                        newline: true,
 6629                    },
 6630                    BracketPair {
 6631                        start: "(".to_string(),
 6632                        end: ")".to_string(),
 6633                        close: true,
 6634                        surround: true,
 6635                        newline: true,
 6636                    },
 6637                    BracketPair {
 6638                        start: "[".to_string(),
 6639                        end: "]".to_string(),
 6640                        close: false,
 6641                        surround: false,
 6642                        newline: true,
 6643                    },
 6644                ],
 6645                ..Default::default()
 6646            },
 6647            autoclose_before: "})]".to_string(),
 6648            ..Default::default()
 6649        },
 6650        Some(tree_sitter_rust::LANGUAGE.into()),
 6651    ));
 6652
 6653    cx.language_registry().add(language.clone());
 6654    cx.update_buffer(|buffer, cx| {
 6655        buffer.set_language(Some(language), cx);
 6656    });
 6657
 6658    cx.set_state(
 6659        &"
 6660            ˇ
 6661            ˇ
 6662            ˇ
 6663        "
 6664        .unindent(),
 6665    );
 6666
 6667    // ensure only matching closing brackets are skipped over
 6668    cx.update_editor(|editor, window, cx| {
 6669        editor.handle_input("}", window, cx);
 6670        editor.move_left(&MoveLeft, window, cx);
 6671        editor.handle_input(")", window, cx);
 6672        editor.move_left(&MoveLeft, window, cx);
 6673    });
 6674    cx.assert_editor_state(
 6675        &"
 6676            ˇ)}
 6677            ˇ)}
 6678            ˇ)}
 6679        "
 6680        .unindent(),
 6681    );
 6682
 6683    // skip-over closing brackets at multiple cursors
 6684    cx.update_editor(|editor, window, cx| {
 6685        editor.handle_input(")", window, cx);
 6686        editor.handle_input("}", window, cx);
 6687    });
 6688    cx.assert_editor_state(
 6689        &"
 6690            )}ˇ
 6691            )}ˇ
 6692            )}ˇ
 6693        "
 6694        .unindent(),
 6695    );
 6696
 6697    // ignore non-close brackets
 6698    cx.update_editor(|editor, window, cx| {
 6699        editor.handle_input("]", window, cx);
 6700        editor.move_left(&MoveLeft, window, cx);
 6701        editor.handle_input("]", window, cx);
 6702    });
 6703    cx.assert_editor_state(
 6704        &"
 6705            )}]ˇ]
 6706            )}]ˇ]
 6707            )}]ˇ]
 6708        "
 6709        .unindent(),
 6710    );
 6711}
 6712
 6713#[gpui::test]
 6714async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6715    init_test(cx, |_| {});
 6716
 6717    let mut cx = EditorTestContext::new(cx).await;
 6718
 6719    let html_language = Arc::new(
 6720        Language::new(
 6721            LanguageConfig {
 6722                name: "HTML".into(),
 6723                brackets: BracketPairConfig {
 6724                    pairs: vec![
 6725                        BracketPair {
 6726                            start: "<".into(),
 6727                            end: ">".into(),
 6728                            close: true,
 6729                            ..Default::default()
 6730                        },
 6731                        BracketPair {
 6732                            start: "{".into(),
 6733                            end: "}".into(),
 6734                            close: true,
 6735                            ..Default::default()
 6736                        },
 6737                        BracketPair {
 6738                            start: "(".into(),
 6739                            end: ")".into(),
 6740                            close: true,
 6741                            ..Default::default()
 6742                        },
 6743                    ],
 6744                    ..Default::default()
 6745                },
 6746                autoclose_before: "})]>".into(),
 6747                ..Default::default()
 6748            },
 6749            Some(tree_sitter_html::LANGUAGE.into()),
 6750        )
 6751        .with_injection_query(
 6752            r#"
 6753            (script_element
 6754                (raw_text) @injection.content
 6755                (#set! injection.language "javascript"))
 6756            "#,
 6757        )
 6758        .unwrap(),
 6759    );
 6760
 6761    let javascript_language = Arc::new(Language::new(
 6762        LanguageConfig {
 6763            name: "JavaScript".into(),
 6764            brackets: BracketPairConfig {
 6765                pairs: vec![
 6766                    BracketPair {
 6767                        start: "/*".into(),
 6768                        end: " */".into(),
 6769                        close: true,
 6770                        ..Default::default()
 6771                    },
 6772                    BracketPair {
 6773                        start: "{".into(),
 6774                        end: "}".into(),
 6775                        close: true,
 6776                        ..Default::default()
 6777                    },
 6778                    BracketPair {
 6779                        start: "(".into(),
 6780                        end: ")".into(),
 6781                        close: true,
 6782                        ..Default::default()
 6783                    },
 6784                ],
 6785                ..Default::default()
 6786            },
 6787            autoclose_before: "})]>".into(),
 6788            ..Default::default()
 6789        },
 6790        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6791    ));
 6792
 6793    cx.language_registry().add(html_language.clone());
 6794    cx.language_registry().add(javascript_language.clone());
 6795
 6796    cx.update_buffer(|buffer, cx| {
 6797        buffer.set_language(Some(html_language), cx);
 6798    });
 6799
 6800    cx.set_state(
 6801        &r#"
 6802            <body>ˇ
 6803                <script>
 6804                    var x = 1;ˇ
 6805                </script>
 6806            </body>ˇ
 6807        "#
 6808        .unindent(),
 6809    );
 6810
 6811    // Precondition: different languages are active at different locations.
 6812    cx.update_editor(|editor, window, cx| {
 6813        let snapshot = editor.snapshot(window, cx);
 6814        let cursors = editor.selections.ranges::<usize>(cx);
 6815        let languages = cursors
 6816            .iter()
 6817            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6818            .collect::<Vec<_>>();
 6819        assert_eq!(
 6820            languages,
 6821            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6822        );
 6823    });
 6824
 6825    // Angle brackets autoclose in HTML, but not JavaScript.
 6826    cx.update_editor(|editor, window, cx| {
 6827        editor.handle_input("<", window, cx);
 6828        editor.handle_input("a", window, cx);
 6829    });
 6830    cx.assert_editor_state(
 6831        &r#"
 6832            <body><aˇ>
 6833                <script>
 6834                    var x = 1;<aˇ
 6835                </script>
 6836            </body><aˇ>
 6837        "#
 6838        .unindent(),
 6839    );
 6840
 6841    // Curly braces and parens autoclose in both HTML and JavaScript.
 6842    cx.update_editor(|editor, window, cx| {
 6843        editor.handle_input(" b=", window, cx);
 6844        editor.handle_input("{", window, cx);
 6845        editor.handle_input("c", window, cx);
 6846        editor.handle_input("(", window, cx);
 6847    });
 6848    cx.assert_editor_state(
 6849        &r#"
 6850            <body><a b={c(ˇ)}>
 6851                <script>
 6852                    var x = 1;<a b={c(ˇ)}
 6853                </script>
 6854            </body><a b={c(ˇ)}>
 6855        "#
 6856        .unindent(),
 6857    );
 6858
 6859    // Brackets that were already autoclosed are skipped.
 6860    cx.update_editor(|editor, window, cx| {
 6861        editor.handle_input(")", window, cx);
 6862        editor.handle_input("d", window, cx);
 6863        editor.handle_input("}", window, cx);
 6864    });
 6865    cx.assert_editor_state(
 6866        &r#"
 6867            <body><a b={c()d}ˇ>
 6868                <script>
 6869                    var x = 1;<a b={c()d}ˇ
 6870                </script>
 6871            </body><a b={c()d}ˇ>
 6872        "#
 6873        .unindent(),
 6874    );
 6875    cx.update_editor(|editor, window, cx| {
 6876        editor.handle_input(">", window, cx);
 6877    });
 6878    cx.assert_editor_state(
 6879        &r#"
 6880            <body><a b={c()d}>ˇ
 6881                <script>
 6882                    var x = 1;<a b={c()d}>ˇ
 6883                </script>
 6884            </body><a b={c()d}>ˇ
 6885        "#
 6886        .unindent(),
 6887    );
 6888
 6889    // Reset
 6890    cx.set_state(
 6891        &r#"
 6892            <body>ˇ
 6893                <script>
 6894                    var x = 1;ˇ
 6895                </script>
 6896            </body>ˇ
 6897        "#
 6898        .unindent(),
 6899    );
 6900
 6901    cx.update_editor(|editor, window, cx| {
 6902        editor.handle_input("<", window, cx);
 6903    });
 6904    cx.assert_editor_state(
 6905        &r#"
 6906            <body><ˇ>
 6907                <script>
 6908                    var x = 1;<ˇ
 6909                </script>
 6910            </body><ˇ>
 6911        "#
 6912        .unindent(),
 6913    );
 6914
 6915    // When backspacing, the closing angle brackets are removed.
 6916    cx.update_editor(|editor, window, cx| {
 6917        editor.backspace(&Backspace, window, cx);
 6918    });
 6919    cx.assert_editor_state(
 6920        &r#"
 6921            <body>ˇ
 6922                <script>
 6923                    var x = 1;ˇ
 6924                </script>
 6925            </body>ˇ
 6926        "#
 6927        .unindent(),
 6928    );
 6929
 6930    // Block comments autoclose in JavaScript, but not HTML.
 6931    cx.update_editor(|editor, window, cx| {
 6932        editor.handle_input("/", window, cx);
 6933        editor.handle_input("*", window, cx);
 6934    });
 6935    cx.assert_editor_state(
 6936        &r#"
 6937            <body>/*ˇ
 6938                <script>
 6939                    var x = 1;/*ˇ */
 6940                </script>
 6941            </body>/*ˇ
 6942        "#
 6943        .unindent(),
 6944    );
 6945}
 6946
 6947#[gpui::test]
 6948async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6949    init_test(cx, |_| {});
 6950
 6951    let mut cx = EditorTestContext::new(cx).await;
 6952
 6953    let rust_language = Arc::new(
 6954        Language::new(
 6955            LanguageConfig {
 6956                name: "Rust".into(),
 6957                brackets: serde_json::from_value(json!([
 6958                    { "start": "{", "end": "}", "close": true, "newline": true },
 6959                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6960                ]))
 6961                .unwrap(),
 6962                autoclose_before: "})]>".into(),
 6963                ..Default::default()
 6964            },
 6965            Some(tree_sitter_rust::LANGUAGE.into()),
 6966        )
 6967        .with_override_query("(string_literal) @string")
 6968        .unwrap(),
 6969    );
 6970
 6971    cx.language_registry().add(rust_language.clone());
 6972    cx.update_buffer(|buffer, cx| {
 6973        buffer.set_language(Some(rust_language), cx);
 6974    });
 6975
 6976    cx.set_state(
 6977        &r#"
 6978            let x = ˇ
 6979        "#
 6980        .unindent(),
 6981    );
 6982
 6983    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6984    cx.update_editor(|editor, window, cx| {
 6985        editor.handle_input("\"", window, cx);
 6986    });
 6987    cx.assert_editor_state(
 6988        &r#"
 6989            let x = "ˇ"
 6990        "#
 6991        .unindent(),
 6992    );
 6993
 6994    // Inserting another quotation mark. The cursor moves across the existing
 6995    // automatically-inserted quotation mark.
 6996    cx.update_editor(|editor, window, cx| {
 6997        editor.handle_input("\"", window, cx);
 6998    });
 6999    cx.assert_editor_state(
 7000        &r#"
 7001            let x = ""ˇ
 7002        "#
 7003        .unindent(),
 7004    );
 7005
 7006    // Reset
 7007    cx.set_state(
 7008        &r#"
 7009            let x = ˇ
 7010        "#
 7011        .unindent(),
 7012    );
 7013
 7014    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7015    cx.update_editor(|editor, window, cx| {
 7016        editor.handle_input("\"", window, cx);
 7017        editor.handle_input(" ", window, cx);
 7018        editor.move_left(&Default::default(), window, cx);
 7019        editor.handle_input("\\", window, cx);
 7020        editor.handle_input("\"", window, cx);
 7021    });
 7022    cx.assert_editor_state(
 7023        &r#"
 7024            let x = "\"ˇ "
 7025        "#
 7026        .unindent(),
 7027    );
 7028
 7029    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7030    // mark. Nothing is inserted.
 7031    cx.update_editor(|editor, window, cx| {
 7032        editor.move_right(&Default::default(), window, cx);
 7033        editor.handle_input("\"", window, cx);
 7034    });
 7035    cx.assert_editor_state(
 7036        &r#"
 7037            let x = "\" "ˇ
 7038        "#
 7039        .unindent(),
 7040    );
 7041}
 7042
 7043#[gpui::test]
 7044async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7045    init_test(cx, |_| {});
 7046
 7047    let language = Arc::new(Language::new(
 7048        LanguageConfig {
 7049            brackets: BracketPairConfig {
 7050                pairs: vec![
 7051                    BracketPair {
 7052                        start: "{".to_string(),
 7053                        end: "}".to_string(),
 7054                        close: true,
 7055                        surround: true,
 7056                        newline: true,
 7057                    },
 7058                    BracketPair {
 7059                        start: "/* ".to_string(),
 7060                        end: "*/".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        ..Default::default()
 7064                    },
 7065                ],
 7066                ..Default::default()
 7067            },
 7068            ..Default::default()
 7069        },
 7070        Some(tree_sitter_rust::LANGUAGE.into()),
 7071    ));
 7072
 7073    let text = r#"
 7074        a
 7075        b
 7076        c
 7077    "#
 7078    .unindent();
 7079
 7080    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7081    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7082    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7083    editor
 7084        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7085        .await;
 7086
 7087    editor.update_in(cx, |editor, window, cx| {
 7088        editor.change_selections(None, window, cx, |s| {
 7089            s.select_display_ranges([
 7090                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7091                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7092                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7093            ])
 7094        });
 7095
 7096        editor.handle_input("{", window, cx);
 7097        editor.handle_input("{", window, cx);
 7098        editor.handle_input("{", window, cx);
 7099        assert_eq!(
 7100            editor.text(cx),
 7101            "
 7102                {{{a}}}
 7103                {{{b}}}
 7104                {{{c}}}
 7105            "
 7106            .unindent()
 7107        );
 7108        assert_eq!(
 7109            editor.selections.display_ranges(cx),
 7110            [
 7111                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7112                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7113                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7114            ]
 7115        );
 7116
 7117        editor.undo(&Undo, window, cx);
 7118        editor.undo(&Undo, window, cx);
 7119        editor.undo(&Undo, window, cx);
 7120        assert_eq!(
 7121            editor.text(cx),
 7122            "
 7123                a
 7124                b
 7125                c
 7126            "
 7127            .unindent()
 7128        );
 7129        assert_eq!(
 7130            editor.selections.display_ranges(cx),
 7131            [
 7132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7133                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7134                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7135            ]
 7136        );
 7137
 7138        // Ensure inserting the first character of a multi-byte bracket pair
 7139        // doesn't surround the selections with the bracket.
 7140        editor.handle_input("/", window, cx);
 7141        assert_eq!(
 7142            editor.text(cx),
 7143            "
 7144                /
 7145                /
 7146                /
 7147            "
 7148            .unindent()
 7149        );
 7150        assert_eq!(
 7151            editor.selections.display_ranges(cx),
 7152            [
 7153                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7154                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7155                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7156            ]
 7157        );
 7158
 7159        editor.undo(&Undo, window, cx);
 7160        assert_eq!(
 7161            editor.text(cx),
 7162            "
 7163                a
 7164                b
 7165                c
 7166            "
 7167            .unindent()
 7168        );
 7169        assert_eq!(
 7170            editor.selections.display_ranges(cx),
 7171            [
 7172                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7173                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7174                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7175            ]
 7176        );
 7177
 7178        // Ensure inserting the last character of a multi-byte bracket pair
 7179        // doesn't surround the selections with the bracket.
 7180        editor.handle_input("*", window, cx);
 7181        assert_eq!(
 7182            editor.text(cx),
 7183            "
 7184                *
 7185                *
 7186                *
 7187            "
 7188            .unindent()
 7189        );
 7190        assert_eq!(
 7191            editor.selections.display_ranges(cx),
 7192            [
 7193                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7194                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7195                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7196            ]
 7197        );
 7198    });
 7199}
 7200
 7201#[gpui::test]
 7202async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7203    init_test(cx, |_| {});
 7204
 7205    let language = Arc::new(Language::new(
 7206        LanguageConfig {
 7207            brackets: BracketPairConfig {
 7208                pairs: vec![BracketPair {
 7209                    start: "{".to_string(),
 7210                    end: "}".to_string(),
 7211                    close: true,
 7212                    surround: true,
 7213                    newline: true,
 7214                }],
 7215                ..Default::default()
 7216            },
 7217            autoclose_before: "}".to_string(),
 7218            ..Default::default()
 7219        },
 7220        Some(tree_sitter_rust::LANGUAGE.into()),
 7221    ));
 7222
 7223    let text = r#"
 7224        a
 7225        b
 7226        c
 7227    "#
 7228    .unindent();
 7229
 7230    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7231    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7232    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7233    editor
 7234        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7235        .await;
 7236
 7237    editor.update_in(cx, |editor, window, cx| {
 7238        editor.change_selections(None, window, cx, |s| {
 7239            s.select_ranges([
 7240                Point::new(0, 1)..Point::new(0, 1),
 7241                Point::new(1, 1)..Point::new(1, 1),
 7242                Point::new(2, 1)..Point::new(2, 1),
 7243            ])
 7244        });
 7245
 7246        editor.handle_input("{", window, cx);
 7247        editor.handle_input("{", window, cx);
 7248        editor.handle_input("_", window, cx);
 7249        assert_eq!(
 7250            editor.text(cx),
 7251            "
 7252                a{{_}}
 7253                b{{_}}
 7254                c{{_}}
 7255            "
 7256            .unindent()
 7257        );
 7258        assert_eq!(
 7259            editor.selections.ranges::<Point>(cx),
 7260            [
 7261                Point::new(0, 4)..Point::new(0, 4),
 7262                Point::new(1, 4)..Point::new(1, 4),
 7263                Point::new(2, 4)..Point::new(2, 4)
 7264            ]
 7265        );
 7266
 7267        editor.backspace(&Default::default(), window, cx);
 7268        editor.backspace(&Default::default(), window, cx);
 7269        assert_eq!(
 7270            editor.text(cx),
 7271            "
 7272                a{}
 7273                b{}
 7274                c{}
 7275            "
 7276            .unindent()
 7277        );
 7278        assert_eq!(
 7279            editor.selections.ranges::<Point>(cx),
 7280            [
 7281                Point::new(0, 2)..Point::new(0, 2),
 7282                Point::new(1, 2)..Point::new(1, 2),
 7283                Point::new(2, 2)..Point::new(2, 2)
 7284            ]
 7285        );
 7286
 7287        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7288        assert_eq!(
 7289            editor.text(cx),
 7290            "
 7291                a
 7292                b
 7293                c
 7294            "
 7295            .unindent()
 7296        );
 7297        assert_eq!(
 7298            editor.selections.ranges::<Point>(cx),
 7299            [
 7300                Point::new(0, 1)..Point::new(0, 1),
 7301                Point::new(1, 1)..Point::new(1, 1),
 7302                Point::new(2, 1)..Point::new(2, 1)
 7303            ]
 7304        );
 7305    });
 7306}
 7307
 7308#[gpui::test]
 7309async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7310    init_test(cx, |settings| {
 7311        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7312    });
 7313
 7314    let mut cx = EditorTestContext::new(cx).await;
 7315
 7316    let language = Arc::new(Language::new(
 7317        LanguageConfig {
 7318            brackets: BracketPairConfig {
 7319                pairs: vec![
 7320                    BracketPair {
 7321                        start: "{".to_string(),
 7322                        end: "}".to_string(),
 7323                        close: true,
 7324                        surround: true,
 7325                        newline: true,
 7326                    },
 7327                    BracketPair {
 7328                        start: "(".to_string(),
 7329                        end: ")".to_string(),
 7330                        close: true,
 7331                        surround: true,
 7332                        newline: true,
 7333                    },
 7334                    BracketPair {
 7335                        start: "[".to_string(),
 7336                        end: "]".to_string(),
 7337                        close: false,
 7338                        surround: true,
 7339                        newline: true,
 7340                    },
 7341                ],
 7342                ..Default::default()
 7343            },
 7344            autoclose_before: "})]".to_string(),
 7345            ..Default::default()
 7346        },
 7347        Some(tree_sitter_rust::LANGUAGE.into()),
 7348    ));
 7349
 7350    cx.language_registry().add(language.clone());
 7351    cx.update_buffer(|buffer, cx| {
 7352        buffer.set_language(Some(language), cx);
 7353    });
 7354
 7355    cx.set_state(
 7356        &"
 7357            {(ˇ)}
 7358            [[ˇ]]
 7359            {(ˇ)}
 7360        "
 7361        .unindent(),
 7362    );
 7363
 7364    cx.update_editor(|editor, window, cx| {
 7365        editor.backspace(&Default::default(), window, cx);
 7366        editor.backspace(&Default::default(), window, cx);
 7367    });
 7368
 7369    cx.assert_editor_state(
 7370        &"
 7371            ˇ
 7372            ˇ]]
 7373            ˇ
 7374        "
 7375        .unindent(),
 7376    );
 7377
 7378    cx.update_editor(|editor, window, cx| {
 7379        editor.handle_input("{", window, cx);
 7380        editor.handle_input("{", window, cx);
 7381        editor.move_right(&MoveRight, window, cx);
 7382        editor.move_right(&MoveRight, window, cx);
 7383        editor.move_left(&MoveLeft, window, cx);
 7384        editor.move_left(&MoveLeft, window, cx);
 7385        editor.backspace(&Default::default(), window, cx);
 7386    });
 7387
 7388    cx.assert_editor_state(
 7389        &"
 7390            {ˇ}
 7391            {ˇ}]]
 7392            {ˇ}
 7393        "
 7394        .unindent(),
 7395    );
 7396
 7397    cx.update_editor(|editor, window, cx| {
 7398        editor.backspace(&Default::default(), window, cx);
 7399    });
 7400
 7401    cx.assert_editor_state(
 7402        &"
 7403            ˇ
 7404            ˇ]]
 7405            ˇ
 7406        "
 7407        .unindent(),
 7408    );
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7413    init_test(cx, |_| {});
 7414
 7415    let language = Arc::new(Language::new(
 7416        LanguageConfig::default(),
 7417        Some(tree_sitter_rust::LANGUAGE.into()),
 7418    ));
 7419
 7420    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7421    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7422    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7423    editor
 7424        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7425        .await;
 7426
 7427    editor.update_in(cx, |editor, window, cx| {
 7428        editor.set_auto_replace_emoji_shortcode(true);
 7429
 7430        editor.handle_input("Hello ", window, cx);
 7431        editor.handle_input(":wave", window, cx);
 7432        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7433
 7434        editor.handle_input(":", window, cx);
 7435        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7436
 7437        editor.handle_input(" :smile", window, cx);
 7438        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7439
 7440        editor.handle_input(":", window, cx);
 7441        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7442
 7443        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7444        editor.handle_input(":wave", window, cx);
 7445        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7446
 7447        editor.handle_input(":", window, cx);
 7448        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7449
 7450        editor.handle_input(":1", window, cx);
 7451        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7452
 7453        editor.handle_input(":", window, cx);
 7454        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7455
 7456        // Ensure shortcode does not get replaced when it is part of a word
 7457        editor.handle_input(" Test:wave", window, cx);
 7458        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7459
 7460        editor.handle_input(":", window, cx);
 7461        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7462
 7463        editor.set_auto_replace_emoji_shortcode(false);
 7464
 7465        // Ensure shortcode does not get replaced when auto replace is off
 7466        editor.handle_input(" :wave", window, cx);
 7467        assert_eq!(
 7468            editor.text(cx),
 7469            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7470        );
 7471
 7472        editor.handle_input(":", window, cx);
 7473        assert_eq!(
 7474            editor.text(cx),
 7475            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7476        );
 7477    });
 7478}
 7479
 7480#[gpui::test]
 7481async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7482    init_test(cx, |_| {});
 7483
 7484    let (text, insertion_ranges) = marked_text_ranges(
 7485        indoc! {"
 7486            ˇ
 7487        "},
 7488        false,
 7489    );
 7490
 7491    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7492    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7493
 7494    _ = editor.update_in(cx, |editor, window, cx| {
 7495        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7496
 7497        editor
 7498            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7499            .unwrap();
 7500
 7501        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7502            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7503            assert_eq!(editor.text(cx), expected_text);
 7504            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7505        }
 7506
 7507        assert(
 7508            editor,
 7509            cx,
 7510            indoc! {"
 7511            type «» =•
 7512            "},
 7513        );
 7514
 7515        assert!(editor.context_menu_visible(), "There should be a matches");
 7516    });
 7517}
 7518
 7519#[gpui::test]
 7520async fn test_snippets(cx: &mut TestAppContext) {
 7521    init_test(cx, |_| {});
 7522
 7523    let (text, insertion_ranges) = marked_text_ranges(
 7524        indoc! {"
 7525            a.ˇ b
 7526            a.ˇ b
 7527            a.ˇ b
 7528        "},
 7529        false,
 7530    );
 7531
 7532    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7533    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7534
 7535    editor.update_in(cx, |editor, window, cx| {
 7536        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7537
 7538        editor
 7539            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7540            .unwrap();
 7541
 7542        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7543            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7544            assert_eq!(editor.text(cx), expected_text);
 7545            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7546        }
 7547
 7548        assert(
 7549            editor,
 7550            cx,
 7551            indoc! {"
 7552                a.f(«one», two, «three») b
 7553                a.f(«one», two, «three») b
 7554                a.f(«one», two, «three») b
 7555            "},
 7556        );
 7557
 7558        // Can't move earlier than the first tab stop
 7559        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7560        assert(
 7561            editor,
 7562            cx,
 7563            indoc! {"
 7564                a.f(«one», two, «three») b
 7565                a.f(«one», two, «three») b
 7566                a.f(«one», two, «three») b
 7567            "},
 7568        );
 7569
 7570        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7571        assert(
 7572            editor,
 7573            cx,
 7574            indoc! {"
 7575                a.f(one, «two», three) b
 7576                a.f(one, «two», three) b
 7577                a.f(one, «two», three) b
 7578            "},
 7579        );
 7580
 7581        editor.move_to_prev_snippet_tabstop(window, cx);
 7582        assert(
 7583            editor,
 7584            cx,
 7585            indoc! {"
 7586                a.f(«one», two, «three») b
 7587                a.f(«one», two, «three») b
 7588                a.f(«one», two, «three») b
 7589            "},
 7590        );
 7591
 7592        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7593        assert(
 7594            editor,
 7595            cx,
 7596            indoc! {"
 7597                a.f(one, «two», three) b
 7598                a.f(one, «two», three) b
 7599                a.f(one, «two», three) b
 7600            "},
 7601        );
 7602        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7603        assert(
 7604            editor,
 7605            cx,
 7606            indoc! {"
 7607                a.f(one, two, three)ˇ b
 7608                a.f(one, two, three)ˇ b
 7609                a.f(one, two, three)ˇ b
 7610            "},
 7611        );
 7612
 7613        // As soon as the last tab stop is reached, snippet state is gone
 7614        editor.move_to_prev_snippet_tabstop(window, cx);
 7615        assert(
 7616            editor,
 7617            cx,
 7618            indoc! {"
 7619                a.f(one, two, three)ˇ b
 7620                a.f(one, two, three)ˇ b
 7621                a.f(one, two, three)ˇ b
 7622            "},
 7623        );
 7624    });
 7625}
 7626
 7627#[gpui::test]
 7628async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7629    init_test(cx, |_| {});
 7630
 7631    let fs = FakeFs::new(cx.executor());
 7632    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7633
 7634    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7635
 7636    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7637    language_registry.add(rust_lang());
 7638    let mut fake_servers = language_registry.register_fake_lsp(
 7639        "Rust",
 7640        FakeLspAdapter {
 7641            capabilities: lsp::ServerCapabilities {
 7642                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7643                ..Default::default()
 7644            },
 7645            ..Default::default()
 7646        },
 7647    );
 7648
 7649    let buffer = project
 7650        .update(cx, |project, cx| {
 7651            project.open_local_buffer(path!("/file.rs"), cx)
 7652        })
 7653        .await
 7654        .unwrap();
 7655
 7656    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7657    let (editor, cx) = cx.add_window_view(|window, cx| {
 7658        build_editor_with_project(project.clone(), buffer, window, cx)
 7659    });
 7660    editor.update_in(cx, |editor, window, cx| {
 7661        editor.set_text("one\ntwo\nthree\n", window, cx)
 7662    });
 7663    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7664
 7665    cx.executor().start_waiting();
 7666    let fake_server = fake_servers.next().await.unwrap();
 7667
 7668    let save = editor
 7669        .update_in(cx, |editor, window, cx| {
 7670            editor.save(true, project.clone(), window, cx)
 7671        })
 7672        .unwrap();
 7673    fake_server
 7674        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7675            assert_eq!(
 7676                params.text_document.uri,
 7677                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7678            );
 7679            assert_eq!(params.options.tab_size, 4);
 7680            Ok(Some(vec![lsp::TextEdit::new(
 7681                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7682                ", ".to_string(),
 7683            )]))
 7684        })
 7685        .next()
 7686        .await;
 7687    cx.executor().start_waiting();
 7688    save.await;
 7689
 7690    assert_eq!(
 7691        editor.update(cx, |editor, cx| editor.text(cx)),
 7692        "one, two\nthree\n"
 7693    );
 7694    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7695
 7696    editor.update_in(cx, |editor, window, cx| {
 7697        editor.set_text("one\ntwo\nthree\n", window, cx)
 7698    });
 7699    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7700
 7701    // Ensure we can still save even if formatting hangs.
 7702    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7703        move |params, _| async move {
 7704            assert_eq!(
 7705                params.text_document.uri,
 7706                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7707            );
 7708            futures::future::pending::<()>().await;
 7709            unreachable!()
 7710        },
 7711    );
 7712    let save = editor
 7713        .update_in(cx, |editor, window, cx| {
 7714            editor.save(true, project.clone(), window, cx)
 7715        })
 7716        .unwrap();
 7717    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7718    cx.executor().start_waiting();
 7719    save.await;
 7720    assert_eq!(
 7721        editor.update(cx, |editor, cx| editor.text(cx)),
 7722        "one\ntwo\nthree\n"
 7723    );
 7724    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7725
 7726    // For non-dirty buffer, no formatting request should be sent
 7727    let save = editor
 7728        .update_in(cx, |editor, window, cx| {
 7729            editor.save(true, project.clone(), window, cx)
 7730        })
 7731        .unwrap();
 7732    let _pending_format_request = fake_server
 7733        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7734            panic!("Should not be invoked on non-dirty buffer");
 7735        })
 7736        .next();
 7737    cx.executor().start_waiting();
 7738    save.await;
 7739
 7740    // Set rust language override and assert overridden tabsize is sent to language server
 7741    update_test_language_settings(cx, |settings| {
 7742        settings.languages.insert(
 7743            "Rust".into(),
 7744            LanguageSettingsContent {
 7745                tab_size: NonZeroU32::new(8),
 7746                ..Default::default()
 7747            },
 7748        );
 7749    });
 7750
 7751    editor.update_in(cx, |editor, window, cx| {
 7752        editor.set_text("somehting_new\n", window, cx)
 7753    });
 7754    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7755    let save = editor
 7756        .update_in(cx, |editor, window, cx| {
 7757            editor.save(true, project.clone(), window, cx)
 7758        })
 7759        .unwrap();
 7760    fake_server
 7761        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7762            assert_eq!(
 7763                params.text_document.uri,
 7764                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7765            );
 7766            assert_eq!(params.options.tab_size, 8);
 7767            Ok(Some(vec![]))
 7768        })
 7769        .next()
 7770        .await;
 7771    cx.executor().start_waiting();
 7772    save.await;
 7773}
 7774
 7775#[gpui::test]
 7776async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7777    init_test(cx, |_| {});
 7778
 7779    let cols = 4;
 7780    let rows = 10;
 7781    let sample_text_1 = sample_text(rows, cols, 'a');
 7782    assert_eq!(
 7783        sample_text_1,
 7784        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7785    );
 7786    let sample_text_2 = sample_text(rows, cols, 'l');
 7787    assert_eq!(
 7788        sample_text_2,
 7789        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7790    );
 7791    let sample_text_3 = sample_text(rows, cols, 'v');
 7792    assert_eq!(
 7793        sample_text_3,
 7794        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7795    );
 7796
 7797    let fs = FakeFs::new(cx.executor());
 7798    fs.insert_tree(
 7799        path!("/a"),
 7800        json!({
 7801            "main.rs": sample_text_1,
 7802            "other.rs": sample_text_2,
 7803            "lib.rs": sample_text_3,
 7804        }),
 7805    )
 7806    .await;
 7807
 7808    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7809    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7810    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7811
 7812    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7813    language_registry.add(rust_lang());
 7814    let mut fake_servers = language_registry.register_fake_lsp(
 7815        "Rust",
 7816        FakeLspAdapter {
 7817            capabilities: lsp::ServerCapabilities {
 7818                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7819                ..Default::default()
 7820            },
 7821            ..Default::default()
 7822        },
 7823    );
 7824
 7825    let worktree = project.update(cx, |project, cx| {
 7826        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7827        assert_eq!(worktrees.len(), 1);
 7828        worktrees.pop().unwrap()
 7829    });
 7830    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7831
 7832    let buffer_1 = project
 7833        .update(cx, |project, cx| {
 7834            project.open_buffer((worktree_id, "main.rs"), cx)
 7835        })
 7836        .await
 7837        .unwrap();
 7838    let buffer_2 = project
 7839        .update(cx, |project, cx| {
 7840            project.open_buffer((worktree_id, "other.rs"), cx)
 7841        })
 7842        .await
 7843        .unwrap();
 7844    let buffer_3 = project
 7845        .update(cx, |project, cx| {
 7846            project.open_buffer((worktree_id, "lib.rs"), cx)
 7847        })
 7848        .await
 7849        .unwrap();
 7850
 7851    let multi_buffer = cx.new(|cx| {
 7852        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7853        multi_buffer.push_excerpts(
 7854            buffer_1.clone(),
 7855            [
 7856                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7857                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7858                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7859            ],
 7860            cx,
 7861        );
 7862        multi_buffer.push_excerpts(
 7863            buffer_2.clone(),
 7864            [
 7865                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7866                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7867                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7868            ],
 7869            cx,
 7870        );
 7871        multi_buffer.push_excerpts(
 7872            buffer_3.clone(),
 7873            [
 7874                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7875                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7876                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7877            ],
 7878            cx,
 7879        );
 7880        multi_buffer
 7881    });
 7882    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7883        Editor::new(
 7884            EditorMode::Full,
 7885            multi_buffer,
 7886            Some(project.clone()),
 7887            window,
 7888            cx,
 7889        )
 7890    });
 7891
 7892    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7893        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7894            s.select_ranges(Some(1..2))
 7895        });
 7896        editor.insert("|one|two|three|", window, cx);
 7897    });
 7898    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7899    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7900        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7901            s.select_ranges(Some(60..70))
 7902        });
 7903        editor.insert("|four|five|six|", window, cx);
 7904    });
 7905    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7906
 7907    // First two buffers should be edited, but not the third one.
 7908    assert_eq!(
 7909        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7910        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7911    );
 7912    buffer_1.update(cx, |buffer, _| {
 7913        assert!(buffer.is_dirty());
 7914        assert_eq!(
 7915            buffer.text(),
 7916            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7917        )
 7918    });
 7919    buffer_2.update(cx, |buffer, _| {
 7920        assert!(buffer.is_dirty());
 7921        assert_eq!(
 7922            buffer.text(),
 7923            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7924        )
 7925    });
 7926    buffer_3.update(cx, |buffer, _| {
 7927        assert!(!buffer.is_dirty());
 7928        assert_eq!(buffer.text(), sample_text_3,)
 7929    });
 7930    cx.executor().run_until_parked();
 7931
 7932    cx.executor().start_waiting();
 7933    let save = multi_buffer_editor
 7934        .update_in(cx, |editor, window, cx| {
 7935            editor.save(true, project.clone(), window, cx)
 7936        })
 7937        .unwrap();
 7938
 7939    let fake_server = fake_servers.next().await.unwrap();
 7940    fake_server
 7941        .server
 7942        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7943            Ok(Some(vec![lsp::TextEdit::new(
 7944                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7945                format!("[{} formatted]", params.text_document.uri),
 7946            )]))
 7947        })
 7948        .detach();
 7949    save.await;
 7950
 7951    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7952    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7953    assert_eq!(
 7954        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7955        uri!(
 7956            "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7957        ),
 7958    );
 7959    buffer_1.update(cx, |buffer, _| {
 7960        assert!(!buffer.is_dirty());
 7961        assert_eq!(
 7962            buffer.text(),
 7963            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7964        )
 7965    });
 7966    buffer_2.update(cx, |buffer, _| {
 7967        assert!(!buffer.is_dirty());
 7968        assert_eq!(
 7969            buffer.text(),
 7970            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7971        )
 7972    });
 7973    buffer_3.update(cx, |buffer, _| {
 7974        assert!(!buffer.is_dirty());
 7975        assert_eq!(buffer.text(), sample_text_3,)
 7976    });
 7977}
 7978
 7979#[gpui::test]
 7980async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7981    init_test(cx, |_| {});
 7982
 7983    let fs = FakeFs::new(cx.executor());
 7984    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7985
 7986    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7987
 7988    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7989    language_registry.add(rust_lang());
 7990    let mut fake_servers = language_registry.register_fake_lsp(
 7991        "Rust",
 7992        FakeLspAdapter {
 7993            capabilities: lsp::ServerCapabilities {
 7994                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7995                ..Default::default()
 7996            },
 7997            ..Default::default()
 7998        },
 7999    );
 8000
 8001    let buffer = project
 8002        .update(cx, |project, cx| {
 8003            project.open_local_buffer(path!("/file.rs"), cx)
 8004        })
 8005        .await
 8006        .unwrap();
 8007
 8008    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8009    let (editor, cx) = cx.add_window_view(|window, cx| {
 8010        build_editor_with_project(project.clone(), buffer, window, cx)
 8011    });
 8012    editor.update_in(cx, |editor, window, cx| {
 8013        editor.set_text("one\ntwo\nthree\n", window, cx)
 8014    });
 8015    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8016
 8017    cx.executor().start_waiting();
 8018    let fake_server = fake_servers.next().await.unwrap();
 8019
 8020    let save = editor
 8021        .update_in(cx, |editor, window, cx| {
 8022            editor.save(true, project.clone(), window, cx)
 8023        })
 8024        .unwrap();
 8025    fake_server
 8026        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8027            assert_eq!(
 8028                params.text_document.uri,
 8029                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8030            );
 8031            assert_eq!(params.options.tab_size, 4);
 8032            Ok(Some(vec![lsp::TextEdit::new(
 8033                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8034                ", ".to_string(),
 8035            )]))
 8036        })
 8037        .next()
 8038        .await;
 8039    cx.executor().start_waiting();
 8040    save.await;
 8041    assert_eq!(
 8042        editor.update(cx, |editor, cx| editor.text(cx)),
 8043        "one, two\nthree\n"
 8044    );
 8045    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8046
 8047    editor.update_in(cx, |editor, window, cx| {
 8048        editor.set_text("one\ntwo\nthree\n", window, cx)
 8049    });
 8050    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8051
 8052    // Ensure we can still save even if formatting hangs.
 8053    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8054        move |params, _| async move {
 8055            assert_eq!(
 8056                params.text_document.uri,
 8057                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8058            );
 8059            futures::future::pending::<()>().await;
 8060            unreachable!()
 8061        },
 8062    );
 8063    let save = editor
 8064        .update_in(cx, |editor, window, cx| {
 8065            editor.save(true, project.clone(), window, cx)
 8066        })
 8067        .unwrap();
 8068    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8069    cx.executor().start_waiting();
 8070    save.await;
 8071    assert_eq!(
 8072        editor.update(cx, |editor, cx| editor.text(cx)),
 8073        "one\ntwo\nthree\n"
 8074    );
 8075    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8076
 8077    // For non-dirty buffer, no formatting request should be sent
 8078    let save = editor
 8079        .update_in(cx, |editor, window, cx| {
 8080            editor.save(true, project.clone(), window, cx)
 8081        })
 8082        .unwrap();
 8083    let _pending_format_request = fake_server
 8084        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8085            panic!("Should not be invoked on non-dirty buffer");
 8086        })
 8087        .next();
 8088    cx.executor().start_waiting();
 8089    save.await;
 8090
 8091    // Set Rust language override and assert overridden tabsize is sent to language server
 8092    update_test_language_settings(cx, |settings| {
 8093        settings.languages.insert(
 8094            "Rust".into(),
 8095            LanguageSettingsContent {
 8096                tab_size: NonZeroU32::new(8),
 8097                ..Default::default()
 8098            },
 8099        );
 8100    });
 8101
 8102    editor.update_in(cx, |editor, window, cx| {
 8103        editor.set_text("somehting_new\n", window, cx)
 8104    });
 8105    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8106    let save = editor
 8107        .update_in(cx, |editor, window, cx| {
 8108            editor.save(true, project.clone(), window, cx)
 8109        })
 8110        .unwrap();
 8111    fake_server
 8112        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8113            assert_eq!(
 8114                params.text_document.uri,
 8115                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8116            );
 8117            assert_eq!(params.options.tab_size, 8);
 8118            Ok(Some(vec![]))
 8119        })
 8120        .next()
 8121        .await;
 8122    cx.executor().start_waiting();
 8123    save.await;
 8124}
 8125
 8126#[gpui::test]
 8127async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8128    init_test(cx, |settings| {
 8129        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8130            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8131        ))
 8132    });
 8133
 8134    let fs = FakeFs::new(cx.executor());
 8135    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8136
 8137    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8138
 8139    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8140    language_registry.add(Arc::new(Language::new(
 8141        LanguageConfig {
 8142            name: "Rust".into(),
 8143            matcher: LanguageMatcher {
 8144                path_suffixes: vec!["rs".to_string()],
 8145                ..Default::default()
 8146            },
 8147            ..LanguageConfig::default()
 8148        },
 8149        Some(tree_sitter_rust::LANGUAGE.into()),
 8150    )));
 8151    update_test_language_settings(cx, |settings| {
 8152        // Enable Prettier formatting for the same buffer, and ensure
 8153        // LSP is called instead of Prettier.
 8154        settings.defaults.prettier = Some(PrettierSettings {
 8155            allowed: true,
 8156            ..PrettierSettings::default()
 8157        });
 8158    });
 8159    let mut fake_servers = language_registry.register_fake_lsp(
 8160        "Rust",
 8161        FakeLspAdapter {
 8162            capabilities: lsp::ServerCapabilities {
 8163                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8164                ..Default::default()
 8165            },
 8166            ..Default::default()
 8167        },
 8168    );
 8169
 8170    let buffer = project
 8171        .update(cx, |project, cx| {
 8172            project.open_local_buffer(path!("/file.rs"), cx)
 8173        })
 8174        .await
 8175        .unwrap();
 8176
 8177    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8178    let (editor, cx) = cx.add_window_view(|window, cx| {
 8179        build_editor_with_project(project.clone(), buffer, window, cx)
 8180    });
 8181    editor.update_in(cx, |editor, window, cx| {
 8182        editor.set_text("one\ntwo\nthree\n", window, cx)
 8183    });
 8184
 8185    cx.executor().start_waiting();
 8186    let fake_server = fake_servers.next().await.unwrap();
 8187
 8188    let format = editor
 8189        .update_in(cx, |editor, window, cx| {
 8190            editor.perform_format(
 8191                project.clone(),
 8192                FormatTrigger::Manual,
 8193                FormatTarget::Buffers,
 8194                window,
 8195                cx,
 8196            )
 8197        })
 8198        .unwrap();
 8199    fake_server
 8200        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8201            assert_eq!(
 8202                params.text_document.uri,
 8203                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8204            );
 8205            assert_eq!(params.options.tab_size, 4);
 8206            Ok(Some(vec![lsp::TextEdit::new(
 8207                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8208                ", ".to_string(),
 8209            )]))
 8210        })
 8211        .next()
 8212        .await;
 8213    cx.executor().start_waiting();
 8214    format.await;
 8215    assert_eq!(
 8216        editor.update(cx, |editor, cx| editor.text(cx)),
 8217        "one, two\nthree\n"
 8218    );
 8219
 8220    editor.update_in(cx, |editor, window, cx| {
 8221        editor.set_text("one\ntwo\nthree\n", window, cx)
 8222    });
 8223    // Ensure we don't lock if formatting hangs.
 8224    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8225        move |params, _| async move {
 8226            assert_eq!(
 8227                params.text_document.uri,
 8228                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8229            );
 8230            futures::future::pending::<()>().await;
 8231            unreachable!()
 8232        },
 8233    );
 8234    let format = editor
 8235        .update_in(cx, |editor, window, cx| {
 8236            editor.perform_format(
 8237                project,
 8238                FormatTrigger::Manual,
 8239                FormatTarget::Buffers,
 8240                window,
 8241                cx,
 8242            )
 8243        })
 8244        .unwrap();
 8245    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8246    cx.executor().start_waiting();
 8247    format.await;
 8248    assert_eq!(
 8249        editor.update(cx, |editor, cx| editor.text(cx)),
 8250        "one\ntwo\nthree\n"
 8251    );
 8252}
 8253
 8254#[gpui::test]
 8255async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8256    init_test(cx, |settings| {
 8257        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8258            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8259        ))
 8260    });
 8261
 8262    let fs = FakeFs::new(cx.executor());
 8263    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8264
 8265    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8266
 8267    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8268    language_registry.add(Arc::new(Language::new(
 8269        LanguageConfig {
 8270            name: "TypeScript".into(),
 8271            matcher: LanguageMatcher {
 8272                path_suffixes: vec!["ts".to_string()],
 8273                ..Default::default()
 8274            },
 8275            ..LanguageConfig::default()
 8276        },
 8277        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8278    )));
 8279    update_test_language_settings(cx, |settings| {
 8280        settings.defaults.prettier = Some(PrettierSettings {
 8281            allowed: true,
 8282            ..PrettierSettings::default()
 8283        });
 8284    });
 8285    let mut fake_servers = language_registry.register_fake_lsp(
 8286        "TypeScript",
 8287        FakeLspAdapter {
 8288            capabilities: lsp::ServerCapabilities {
 8289                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8290                ..Default::default()
 8291            },
 8292            ..Default::default()
 8293        },
 8294    );
 8295
 8296    let buffer = project
 8297        .update(cx, |project, cx| {
 8298            project.open_local_buffer(path!("/file.ts"), cx)
 8299        })
 8300        .await
 8301        .unwrap();
 8302
 8303    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8304    let (editor, cx) = cx.add_window_view(|window, cx| {
 8305        build_editor_with_project(project.clone(), buffer, window, cx)
 8306    });
 8307    editor.update_in(cx, |editor, window, cx| {
 8308        editor.set_text(
 8309            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8310            window,
 8311            cx,
 8312        )
 8313    });
 8314
 8315    cx.executor().start_waiting();
 8316    let fake_server = fake_servers.next().await.unwrap();
 8317
 8318    let format = editor
 8319        .update_in(cx, |editor, window, cx| {
 8320            editor.perform_code_action_kind(
 8321                project.clone(),
 8322                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8323                window,
 8324                cx,
 8325            )
 8326        })
 8327        .unwrap();
 8328    fake_server
 8329        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8330            assert_eq!(
 8331                params.text_document.uri,
 8332                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8333            );
 8334            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8335                lsp::CodeAction {
 8336                    title: "Organize Imports".to_string(),
 8337                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8338                    edit: Some(lsp::WorkspaceEdit {
 8339                        changes: Some(
 8340                            [(
 8341                                params.text_document.uri.clone(),
 8342                                vec![lsp::TextEdit::new(
 8343                                    lsp::Range::new(
 8344                                        lsp::Position::new(1, 0),
 8345                                        lsp::Position::new(2, 0),
 8346                                    ),
 8347                                    "".to_string(),
 8348                                )],
 8349                            )]
 8350                            .into_iter()
 8351                            .collect(),
 8352                        ),
 8353                        ..Default::default()
 8354                    }),
 8355                    ..Default::default()
 8356                },
 8357            )]))
 8358        })
 8359        .next()
 8360        .await;
 8361    cx.executor().start_waiting();
 8362    format.await;
 8363    assert_eq!(
 8364        editor.update(cx, |editor, cx| editor.text(cx)),
 8365        "import { a } from 'module';\n\nconst x = a;\n"
 8366    );
 8367
 8368    editor.update_in(cx, |editor, window, cx| {
 8369        editor.set_text(
 8370            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8371            window,
 8372            cx,
 8373        )
 8374    });
 8375    // Ensure we don't lock if code action hangs.
 8376    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8377        move |params, _| async move {
 8378            assert_eq!(
 8379                params.text_document.uri,
 8380                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8381            );
 8382            futures::future::pending::<()>().await;
 8383            unreachable!()
 8384        },
 8385    );
 8386    let format = editor
 8387        .update_in(cx, |editor, window, cx| {
 8388            editor.perform_code_action_kind(
 8389                project,
 8390                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8391                window,
 8392                cx,
 8393            )
 8394        })
 8395        .unwrap();
 8396    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8397    cx.executor().start_waiting();
 8398    format.await;
 8399    assert_eq!(
 8400        editor.update(cx, |editor, cx| editor.text(cx)),
 8401        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8402    );
 8403}
 8404
 8405#[gpui::test]
 8406async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8407    init_test(cx, |_| {});
 8408
 8409    let mut cx = EditorLspTestContext::new_rust(
 8410        lsp::ServerCapabilities {
 8411            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8412            ..Default::default()
 8413        },
 8414        cx,
 8415    )
 8416    .await;
 8417
 8418    cx.set_state(indoc! {"
 8419        one.twoˇ
 8420    "});
 8421
 8422    // The format request takes a long time. When it completes, it inserts
 8423    // a newline and an indent before the `.`
 8424    cx.lsp
 8425        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8426            let executor = cx.background_executor().clone();
 8427            async move {
 8428                executor.timer(Duration::from_millis(100)).await;
 8429                Ok(Some(vec![lsp::TextEdit {
 8430                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8431                    new_text: "\n    ".into(),
 8432                }]))
 8433            }
 8434        });
 8435
 8436    // Submit a format request.
 8437    let format_1 = cx
 8438        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8439        .unwrap();
 8440    cx.executor().run_until_parked();
 8441
 8442    // Submit a second format request.
 8443    let format_2 = cx
 8444        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8445        .unwrap();
 8446    cx.executor().run_until_parked();
 8447
 8448    // Wait for both format requests to complete
 8449    cx.executor().advance_clock(Duration::from_millis(200));
 8450    cx.executor().start_waiting();
 8451    format_1.await.unwrap();
 8452    cx.executor().start_waiting();
 8453    format_2.await.unwrap();
 8454
 8455    // The formatting edits only happens once.
 8456    cx.assert_editor_state(indoc! {"
 8457        one
 8458            .twoˇ
 8459    "});
 8460}
 8461
 8462#[gpui::test]
 8463async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8464    init_test(cx, |settings| {
 8465        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8466    });
 8467
 8468    let mut cx = EditorLspTestContext::new_rust(
 8469        lsp::ServerCapabilities {
 8470            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8471            ..Default::default()
 8472        },
 8473        cx,
 8474    )
 8475    .await;
 8476
 8477    // Set up a buffer white some trailing whitespace and no trailing newline.
 8478    cx.set_state(
 8479        &[
 8480            "one ",   //
 8481            "twoˇ",   //
 8482            "three ", //
 8483            "four",   //
 8484        ]
 8485        .join("\n"),
 8486    );
 8487
 8488    // Submit a format request.
 8489    let format = cx
 8490        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8491        .unwrap();
 8492
 8493    // Record which buffer changes have been sent to the language server
 8494    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8495    cx.lsp
 8496        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8497            let buffer_changes = buffer_changes.clone();
 8498            move |params, _| {
 8499                buffer_changes.lock().extend(
 8500                    params
 8501                        .content_changes
 8502                        .into_iter()
 8503                        .map(|e| (e.range.unwrap(), e.text)),
 8504                );
 8505            }
 8506        });
 8507
 8508    // Handle formatting requests to the language server.
 8509    cx.lsp
 8510        .set_request_handler::<lsp::request::Formatting, _, _>({
 8511            let buffer_changes = buffer_changes.clone();
 8512            move |_, _| {
 8513                // When formatting is requested, trailing whitespace has already been stripped,
 8514                // and the trailing newline has already been added.
 8515                assert_eq!(
 8516                    &buffer_changes.lock()[1..],
 8517                    &[
 8518                        (
 8519                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8520                            "".into()
 8521                        ),
 8522                        (
 8523                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8524                            "".into()
 8525                        ),
 8526                        (
 8527                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8528                            "\n".into()
 8529                        ),
 8530                    ]
 8531                );
 8532
 8533                // Insert blank lines between each line of the buffer.
 8534                async move {
 8535                    Ok(Some(vec![
 8536                        lsp::TextEdit {
 8537                            range: lsp::Range::new(
 8538                                lsp::Position::new(1, 0),
 8539                                lsp::Position::new(1, 0),
 8540                            ),
 8541                            new_text: "\n".into(),
 8542                        },
 8543                        lsp::TextEdit {
 8544                            range: lsp::Range::new(
 8545                                lsp::Position::new(2, 0),
 8546                                lsp::Position::new(2, 0),
 8547                            ),
 8548                            new_text: "\n".into(),
 8549                        },
 8550                    ]))
 8551                }
 8552            }
 8553        });
 8554
 8555    // After formatting the buffer, the trailing whitespace is stripped,
 8556    // a newline is appended, and the edits provided by the language server
 8557    // have been applied.
 8558    format.await.unwrap();
 8559    cx.assert_editor_state(
 8560        &[
 8561            "one",   //
 8562            "",      //
 8563            "twoˇ",  //
 8564            "",      //
 8565            "three", //
 8566            "four",  //
 8567            "",      //
 8568        ]
 8569        .join("\n"),
 8570    );
 8571
 8572    // Undoing the formatting undoes the trailing whitespace removal, the
 8573    // trailing newline, and the LSP edits.
 8574    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8575    cx.assert_editor_state(
 8576        &[
 8577            "one ",   //
 8578            "twoˇ",   //
 8579            "three ", //
 8580            "four",   //
 8581        ]
 8582        .join("\n"),
 8583    );
 8584}
 8585
 8586#[gpui::test]
 8587async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8588    cx: &mut TestAppContext,
 8589) {
 8590    init_test(cx, |_| {});
 8591
 8592    cx.update(|cx| {
 8593        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8594            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8595                settings.auto_signature_help = Some(true);
 8596            });
 8597        });
 8598    });
 8599
 8600    let mut cx = EditorLspTestContext::new_rust(
 8601        lsp::ServerCapabilities {
 8602            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8603                ..Default::default()
 8604            }),
 8605            ..Default::default()
 8606        },
 8607        cx,
 8608    )
 8609    .await;
 8610
 8611    let language = Language::new(
 8612        LanguageConfig {
 8613            name: "Rust".into(),
 8614            brackets: BracketPairConfig {
 8615                pairs: vec![
 8616                    BracketPair {
 8617                        start: "{".to_string(),
 8618                        end: "}".to_string(),
 8619                        close: true,
 8620                        surround: true,
 8621                        newline: true,
 8622                    },
 8623                    BracketPair {
 8624                        start: "(".to_string(),
 8625                        end: ")".to_string(),
 8626                        close: true,
 8627                        surround: true,
 8628                        newline: true,
 8629                    },
 8630                    BracketPair {
 8631                        start: "/*".to_string(),
 8632                        end: " */".to_string(),
 8633                        close: true,
 8634                        surround: true,
 8635                        newline: true,
 8636                    },
 8637                    BracketPair {
 8638                        start: "[".to_string(),
 8639                        end: "]".to_string(),
 8640                        close: false,
 8641                        surround: false,
 8642                        newline: true,
 8643                    },
 8644                    BracketPair {
 8645                        start: "\"".to_string(),
 8646                        end: "\"".to_string(),
 8647                        close: true,
 8648                        surround: true,
 8649                        newline: false,
 8650                    },
 8651                    BracketPair {
 8652                        start: "<".to_string(),
 8653                        end: ">".to_string(),
 8654                        close: false,
 8655                        surround: true,
 8656                        newline: true,
 8657                    },
 8658                ],
 8659                ..Default::default()
 8660            },
 8661            autoclose_before: "})]".to_string(),
 8662            ..Default::default()
 8663        },
 8664        Some(tree_sitter_rust::LANGUAGE.into()),
 8665    );
 8666    let language = Arc::new(language);
 8667
 8668    cx.language_registry().add(language.clone());
 8669    cx.update_buffer(|buffer, cx| {
 8670        buffer.set_language(Some(language), cx);
 8671    });
 8672
 8673    cx.set_state(
 8674        &r#"
 8675            fn main() {
 8676                sampleˇ
 8677            }
 8678        "#
 8679        .unindent(),
 8680    );
 8681
 8682    cx.update_editor(|editor, window, cx| {
 8683        editor.handle_input("(", window, cx);
 8684    });
 8685    cx.assert_editor_state(
 8686        &"
 8687            fn main() {
 8688                sample(ˇ)
 8689            }
 8690        "
 8691        .unindent(),
 8692    );
 8693
 8694    let mocked_response = lsp::SignatureHelp {
 8695        signatures: vec![lsp::SignatureInformation {
 8696            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8697            documentation: None,
 8698            parameters: Some(vec![
 8699                lsp::ParameterInformation {
 8700                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8701                    documentation: None,
 8702                },
 8703                lsp::ParameterInformation {
 8704                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8705                    documentation: None,
 8706                },
 8707            ]),
 8708            active_parameter: None,
 8709        }],
 8710        active_signature: Some(0),
 8711        active_parameter: Some(0),
 8712    };
 8713    handle_signature_help_request(&mut cx, mocked_response).await;
 8714
 8715    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8716        .await;
 8717
 8718    cx.editor(|editor, _, _| {
 8719        let signature_help_state = editor.signature_help_state.popover().cloned();
 8720        assert_eq!(
 8721            signature_help_state.unwrap().label,
 8722            "param1: u8, param2: u8"
 8723        );
 8724    });
 8725}
 8726
 8727#[gpui::test]
 8728async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8729    init_test(cx, |_| {});
 8730
 8731    cx.update(|cx| {
 8732        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8733            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8734                settings.auto_signature_help = Some(false);
 8735                settings.show_signature_help_after_edits = Some(false);
 8736            });
 8737        });
 8738    });
 8739
 8740    let mut cx = EditorLspTestContext::new_rust(
 8741        lsp::ServerCapabilities {
 8742            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8743                ..Default::default()
 8744            }),
 8745            ..Default::default()
 8746        },
 8747        cx,
 8748    )
 8749    .await;
 8750
 8751    let language = Language::new(
 8752        LanguageConfig {
 8753            name: "Rust".into(),
 8754            brackets: BracketPairConfig {
 8755                pairs: vec![
 8756                    BracketPair {
 8757                        start: "{".to_string(),
 8758                        end: "}".to_string(),
 8759                        close: true,
 8760                        surround: true,
 8761                        newline: true,
 8762                    },
 8763                    BracketPair {
 8764                        start: "(".to_string(),
 8765                        end: ")".to_string(),
 8766                        close: true,
 8767                        surround: true,
 8768                        newline: true,
 8769                    },
 8770                    BracketPair {
 8771                        start: "/*".to_string(),
 8772                        end: " */".to_string(),
 8773                        close: true,
 8774                        surround: true,
 8775                        newline: true,
 8776                    },
 8777                    BracketPair {
 8778                        start: "[".to_string(),
 8779                        end: "]".to_string(),
 8780                        close: false,
 8781                        surround: false,
 8782                        newline: true,
 8783                    },
 8784                    BracketPair {
 8785                        start: "\"".to_string(),
 8786                        end: "\"".to_string(),
 8787                        close: true,
 8788                        surround: true,
 8789                        newline: false,
 8790                    },
 8791                    BracketPair {
 8792                        start: "<".to_string(),
 8793                        end: ">".to_string(),
 8794                        close: false,
 8795                        surround: true,
 8796                        newline: true,
 8797                    },
 8798                ],
 8799                ..Default::default()
 8800            },
 8801            autoclose_before: "})]".to_string(),
 8802            ..Default::default()
 8803        },
 8804        Some(tree_sitter_rust::LANGUAGE.into()),
 8805    );
 8806    let language = Arc::new(language);
 8807
 8808    cx.language_registry().add(language.clone());
 8809    cx.update_buffer(|buffer, cx| {
 8810        buffer.set_language(Some(language), cx);
 8811    });
 8812
 8813    // Ensure that signature_help is not called when no signature help is enabled.
 8814    cx.set_state(
 8815        &r#"
 8816            fn main() {
 8817                sampleˇ
 8818            }
 8819        "#
 8820        .unindent(),
 8821    );
 8822    cx.update_editor(|editor, window, cx| {
 8823        editor.handle_input("(", window, cx);
 8824    });
 8825    cx.assert_editor_state(
 8826        &"
 8827            fn main() {
 8828                sample(ˇ)
 8829            }
 8830        "
 8831        .unindent(),
 8832    );
 8833    cx.editor(|editor, _, _| {
 8834        assert!(editor.signature_help_state.task().is_none());
 8835    });
 8836
 8837    let mocked_response = lsp::SignatureHelp {
 8838        signatures: vec![lsp::SignatureInformation {
 8839            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8840            documentation: None,
 8841            parameters: Some(vec![
 8842                lsp::ParameterInformation {
 8843                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8844                    documentation: None,
 8845                },
 8846                lsp::ParameterInformation {
 8847                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8848                    documentation: None,
 8849                },
 8850            ]),
 8851            active_parameter: None,
 8852        }],
 8853        active_signature: Some(0),
 8854        active_parameter: Some(0),
 8855    };
 8856
 8857    // Ensure that signature_help is called when enabled afte edits
 8858    cx.update(|_, cx| {
 8859        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8860            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8861                settings.auto_signature_help = Some(false);
 8862                settings.show_signature_help_after_edits = Some(true);
 8863            });
 8864        });
 8865    });
 8866    cx.set_state(
 8867        &r#"
 8868            fn main() {
 8869                sampleˇ
 8870            }
 8871        "#
 8872        .unindent(),
 8873    );
 8874    cx.update_editor(|editor, window, cx| {
 8875        editor.handle_input("(", window, cx);
 8876    });
 8877    cx.assert_editor_state(
 8878        &"
 8879            fn main() {
 8880                sample(ˇ)
 8881            }
 8882        "
 8883        .unindent(),
 8884    );
 8885    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8887        .await;
 8888    cx.update_editor(|editor, _, _| {
 8889        let signature_help_state = editor.signature_help_state.popover().cloned();
 8890        assert!(signature_help_state.is_some());
 8891        assert_eq!(
 8892            signature_help_state.unwrap().label,
 8893            "param1: u8, param2: u8"
 8894        );
 8895        editor.signature_help_state = SignatureHelpState::default();
 8896    });
 8897
 8898    // Ensure that signature_help is called when auto signature help override is enabled
 8899    cx.update(|_, cx| {
 8900        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8901            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8902                settings.auto_signature_help = Some(true);
 8903                settings.show_signature_help_after_edits = Some(false);
 8904            });
 8905        });
 8906    });
 8907    cx.set_state(
 8908        &r#"
 8909            fn main() {
 8910                sampleˇ
 8911            }
 8912        "#
 8913        .unindent(),
 8914    );
 8915    cx.update_editor(|editor, window, cx| {
 8916        editor.handle_input("(", window, cx);
 8917    });
 8918    cx.assert_editor_state(
 8919        &"
 8920            fn main() {
 8921                sample(ˇ)
 8922            }
 8923        "
 8924        .unindent(),
 8925    );
 8926    handle_signature_help_request(&mut cx, mocked_response).await;
 8927    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8928        .await;
 8929    cx.editor(|editor, _, _| {
 8930        let signature_help_state = editor.signature_help_state.popover().cloned();
 8931        assert!(signature_help_state.is_some());
 8932        assert_eq!(
 8933            signature_help_state.unwrap().label,
 8934            "param1: u8, param2: u8"
 8935        );
 8936    });
 8937}
 8938
 8939#[gpui::test]
 8940async fn test_signature_help(cx: &mut TestAppContext) {
 8941    init_test(cx, |_| {});
 8942    cx.update(|cx| {
 8943        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8944            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8945                settings.auto_signature_help = Some(true);
 8946            });
 8947        });
 8948    });
 8949
 8950    let mut cx = EditorLspTestContext::new_rust(
 8951        lsp::ServerCapabilities {
 8952            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8953                ..Default::default()
 8954            }),
 8955            ..Default::default()
 8956        },
 8957        cx,
 8958    )
 8959    .await;
 8960
 8961    // A test that directly calls `show_signature_help`
 8962    cx.update_editor(|editor, window, cx| {
 8963        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8964    });
 8965
 8966    let mocked_response = lsp::SignatureHelp {
 8967        signatures: vec![lsp::SignatureInformation {
 8968            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8969            documentation: None,
 8970            parameters: Some(vec![
 8971                lsp::ParameterInformation {
 8972                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8973                    documentation: None,
 8974                },
 8975                lsp::ParameterInformation {
 8976                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8977                    documentation: None,
 8978                },
 8979            ]),
 8980            active_parameter: None,
 8981        }],
 8982        active_signature: Some(0),
 8983        active_parameter: Some(0),
 8984    };
 8985    handle_signature_help_request(&mut cx, mocked_response).await;
 8986
 8987    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8988        .await;
 8989
 8990    cx.editor(|editor, _, _| {
 8991        let signature_help_state = editor.signature_help_state.popover().cloned();
 8992        assert!(signature_help_state.is_some());
 8993        assert_eq!(
 8994            signature_help_state.unwrap().label,
 8995            "param1: u8, param2: u8"
 8996        );
 8997    });
 8998
 8999    // When exiting outside from inside the brackets, `signature_help` is closed.
 9000    cx.set_state(indoc! {"
 9001        fn main() {
 9002            sample(ˇ);
 9003        }
 9004
 9005        fn sample(param1: u8, param2: u8) {}
 9006    "});
 9007
 9008    cx.update_editor(|editor, window, cx| {
 9009        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9010    });
 9011
 9012    let mocked_response = lsp::SignatureHelp {
 9013        signatures: Vec::new(),
 9014        active_signature: None,
 9015        active_parameter: None,
 9016    };
 9017    handle_signature_help_request(&mut cx, mocked_response).await;
 9018
 9019    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9020        .await;
 9021
 9022    cx.editor(|editor, _, _| {
 9023        assert!(!editor.signature_help_state.is_shown());
 9024    });
 9025
 9026    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9027    cx.set_state(indoc! {"
 9028        fn main() {
 9029            sample(ˇ);
 9030        }
 9031
 9032        fn sample(param1: u8, param2: u8) {}
 9033    "});
 9034
 9035    let mocked_response = lsp::SignatureHelp {
 9036        signatures: vec![lsp::SignatureInformation {
 9037            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9038            documentation: None,
 9039            parameters: Some(vec![
 9040                lsp::ParameterInformation {
 9041                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9042                    documentation: None,
 9043                },
 9044                lsp::ParameterInformation {
 9045                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9046                    documentation: None,
 9047                },
 9048            ]),
 9049            active_parameter: None,
 9050        }],
 9051        active_signature: Some(0),
 9052        active_parameter: Some(0),
 9053    };
 9054    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9055    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9056        .await;
 9057    cx.editor(|editor, _, _| {
 9058        assert!(editor.signature_help_state.is_shown());
 9059    });
 9060
 9061    // Restore the popover with more parameter input
 9062    cx.set_state(indoc! {"
 9063        fn main() {
 9064            sample(param1, param2ˇ);
 9065        }
 9066
 9067        fn sample(param1: u8, param2: u8) {}
 9068    "});
 9069
 9070    let mocked_response = lsp::SignatureHelp {
 9071        signatures: vec![lsp::SignatureInformation {
 9072            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9073            documentation: None,
 9074            parameters: Some(vec![
 9075                lsp::ParameterInformation {
 9076                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9077                    documentation: None,
 9078                },
 9079                lsp::ParameterInformation {
 9080                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9081                    documentation: None,
 9082                },
 9083            ]),
 9084            active_parameter: None,
 9085        }],
 9086        active_signature: Some(0),
 9087        active_parameter: Some(1),
 9088    };
 9089    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9090    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9091        .await;
 9092
 9093    // When selecting a range, the popover is gone.
 9094    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9095    cx.update_editor(|editor, window, cx| {
 9096        editor.change_selections(None, window, cx, |s| {
 9097            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9098        })
 9099    });
 9100    cx.assert_editor_state(indoc! {"
 9101        fn main() {
 9102            sample(param1, «ˇparam2»);
 9103        }
 9104
 9105        fn sample(param1: u8, param2: u8) {}
 9106    "});
 9107    cx.editor(|editor, _, _| {
 9108        assert!(!editor.signature_help_state.is_shown());
 9109    });
 9110
 9111    // When unselecting again, the popover is back if within the brackets.
 9112    cx.update_editor(|editor, window, cx| {
 9113        editor.change_selections(None, window, cx, |s| {
 9114            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9115        })
 9116    });
 9117    cx.assert_editor_state(indoc! {"
 9118        fn main() {
 9119            sample(param1, ˇparam2);
 9120        }
 9121
 9122        fn sample(param1: u8, param2: u8) {}
 9123    "});
 9124    handle_signature_help_request(&mut cx, mocked_response).await;
 9125    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9126        .await;
 9127    cx.editor(|editor, _, _| {
 9128        assert!(editor.signature_help_state.is_shown());
 9129    });
 9130
 9131    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9132    cx.update_editor(|editor, window, cx| {
 9133        editor.change_selections(None, window, cx, |s| {
 9134            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9135            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9136        })
 9137    });
 9138    cx.assert_editor_state(indoc! {"
 9139        fn main() {
 9140            sample(param1, ˇparam2);
 9141        }
 9142
 9143        fn sample(param1: u8, param2: u8) {}
 9144    "});
 9145
 9146    let mocked_response = lsp::SignatureHelp {
 9147        signatures: vec![lsp::SignatureInformation {
 9148            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9149            documentation: None,
 9150            parameters: Some(vec![
 9151                lsp::ParameterInformation {
 9152                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9153                    documentation: None,
 9154                },
 9155                lsp::ParameterInformation {
 9156                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9157                    documentation: None,
 9158                },
 9159            ]),
 9160            active_parameter: None,
 9161        }],
 9162        active_signature: Some(0),
 9163        active_parameter: Some(1),
 9164    };
 9165    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9166    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9167        .await;
 9168    cx.update_editor(|editor, _, cx| {
 9169        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9170    });
 9171    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9172        .await;
 9173    cx.update_editor(|editor, window, cx| {
 9174        editor.change_selections(None, window, cx, |s| {
 9175            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9176        })
 9177    });
 9178    cx.assert_editor_state(indoc! {"
 9179        fn main() {
 9180            sample(param1, «ˇparam2»);
 9181        }
 9182
 9183        fn sample(param1: u8, param2: u8) {}
 9184    "});
 9185    cx.update_editor(|editor, window, cx| {
 9186        editor.change_selections(None, window, cx, |s| {
 9187            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9188        })
 9189    });
 9190    cx.assert_editor_state(indoc! {"
 9191        fn main() {
 9192            sample(param1, ˇparam2);
 9193        }
 9194
 9195        fn sample(param1: u8, param2: u8) {}
 9196    "});
 9197    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9198        .await;
 9199}
 9200
 9201#[gpui::test]
 9202async fn test_completion_mode(cx: &mut TestAppContext) {
 9203    init_test(cx, |_| {});
 9204    let mut cx = EditorLspTestContext::new_rust(
 9205        lsp::ServerCapabilities {
 9206            completion_provider: Some(lsp::CompletionOptions {
 9207                resolve_provider: Some(true),
 9208                ..Default::default()
 9209            }),
 9210            ..Default::default()
 9211        },
 9212        cx,
 9213    )
 9214    .await;
 9215
 9216    struct Run {
 9217        run_description: &'static str,
 9218        initial_state: String,
 9219        buffer_marked_text: String,
 9220        completion_text: &'static str,
 9221        expected_with_insertion_mode: String,
 9222        expected_with_replace_mode: String,
 9223        expected_with_replace_subsequence_mode: String,
 9224        expected_with_replace_suffix_mode: String,
 9225    }
 9226
 9227    let runs = [
 9228        Run {
 9229            run_description: "Start of word matches completion text",
 9230            initial_state: "before ediˇ after".into(),
 9231            buffer_marked_text: "before <edi|> after".into(),
 9232            completion_text: "editor",
 9233            expected_with_insertion_mode: "before editorˇ after".into(),
 9234            expected_with_replace_mode: "before editorˇ after".into(),
 9235            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9236            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9237        },
 9238        Run {
 9239            run_description: "Accept same text at the middle of the word",
 9240            initial_state: "before ediˇtor after".into(),
 9241            buffer_marked_text: "before <edi|tor> after".into(),
 9242            completion_text: "editor",
 9243            expected_with_insertion_mode: "before editorˇtor after".into(),
 9244            expected_with_replace_mode: "before ediˇtor after".into(),
 9245            expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
 9246            expected_with_replace_suffix_mode: "before ediˇtor after".into(),
 9247        },
 9248        Run {
 9249            run_description: "End of word matches completion text -- cursor at end",
 9250            initial_state: "before torˇ after".into(),
 9251            buffer_marked_text: "before <tor|> after".into(),
 9252            completion_text: "editor",
 9253            expected_with_insertion_mode: "before editorˇ after".into(),
 9254            expected_with_replace_mode: "before editorˇ after".into(),
 9255            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9256            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9257        },
 9258        Run {
 9259            run_description: "End of word matches completion text -- cursor at start",
 9260            initial_state: "before ˇtor after".into(),
 9261            buffer_marked_text: "before <|tor> after".into(),
 9262            completion_text: "editor",
 9263            expected_with_insertion_mode: "before editorˇtor after".into(),
 9264            expected_with_replace_mode: "before editorˇ after".into(),
 9265            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9266            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9267        },
 9268        Run {
 9269            run_description: "Prepend text containing whitespace",
 9270            initial_state: "pˇfield: bool".into(),
 9271            buffer_marked_text: "<p|field>: bool".into(),
 9272            completion_text: "pub ",
 9273            expected_with_insertion_mode: "pub ˇfield: bool".into(),
 9274            expected_with_replace_mode: "pub ˇ: bool".into(),
 9275            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9276            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9277        },
 9278        Run {
 9279            run_description: "Add element to start of list",
 9280            initial_state: "[element_ˇelement_2]".into(),
 9281            buffer_marked_text: "[<element_|element_2>]".into(),
 9282            completion_text: "element_1",
 9283            expected_with_insertion_mode: "[element_1ˇelement_2]".into(),
 9284            expected_with_replace_mode: "[element_1ˇ]".into(),
 9285            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9286            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9287        },
 9288        Run {
 9289            run_description: "Add element to start of list -- first and second elements are equal",
 9290            initial_state: "[elˇelement]".into(),
 9291            buffer_marked_text: "[<el|element>]".into(),
 9292            completion_text: "element",
 9293            expected_with_insertion_mode: "[elementˇelement]".into(),
 9294            expected_with_replace_mode: "[elˇement]".into(),
 9295            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9296            expected_with_replace_suffix_mode: "[elˇement]".into(),
 9297        },
 9298        Run {
 9299            run_description: "Ends with matching suffix",
 9300            initial_state: "SubˇError".into(),
 9301            buffer_marked_text: "<Sub|Error>".into(),
 9302            completion_text: "SubscriptionError",
 9303            expected_with_insertion_mode: "SubscriptionErrorˇError".into(),
 9304            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9305            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9306            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9307        },
 9308        Run {
 9309            run_description: "Suffix is a subsequence -- contiguous",
 9310            initial_state: "SubˇErr".into(),
 9311            buffer_marked_text: "<Sub|Err>".into(),
 9312            completion_text: "SubscriptionError",
 9313            expected_with_insertion_mode: "SubscriptionErrorˇErr".into(),
 9314            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9315            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9316            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9317        },
 9318        Run {
 9319            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9320            initial_state: "Suˇscrirr".into(),
 9321            buffer_marked_text: "<Su|scrirr>".into(),
 9322            completion_text: "SubscriptionError",
 9323            expected_with_insertion_mode: "SubscriptionErrorˇscrirr".into(),
 9324            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9325            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9326            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9327        },
 9328        Run {
 9329            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9330            initial_state: "foo(indˇix)".into(),
 9331            buffer_marked_text: "foo(<ind|ix>)".into(),
 9332            completion_text: "node_index",
 9333            expected_with_insertion_mode: "foo(node_indexˇix)".into(),
 9334            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9335            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9336            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9337        },
 9338    ];
 9339
 9340    for run in runs {
 9341        let run_variations = [
 9342            (LspInsertMode::Insert, run.expected_with_insertion_mode),
 9343            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9344            (
 9345                LspInsertMode::ReplaceSubsequence,
 9346                run.expected_with_replace_subsequence_mode,
 9347            ),
 9348            (
 9349                LspInsertMode::ReplaceSuffix,
 9350                run.expected_with_replace_suffix_mode,
 9351            ),
 9352        ];
 9353
 9354        for (lsp_insert_mode, expected_text) in run_variations {
 9355            eprintln!(
 9356                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9357                run.run_description,
 9358            );
 9359
 9360            update_test_language_settings(&mut cx, |settings| {
 9361                settings.defaults.completions = Some(CompletionSettings {
 9362                    lsp_insert_mode,
 9363                    words: WordsCompletionMode::Disabled,
 9364                    lsp: true,
 9365                    lsp_fetch_timeout_ms: 0,
 9366                });
 9367            });
 9368
 9369            cx.set_state(&run.initial_state);
 9370            cx.update_editor(|editor, window, cx| {
 9371                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9372            });
 9373
 9374            let counter = Arc::new(AtomicUsize::new(0));
 9375            handle_completion_request_with_insert_and_replace(
 9376                &mut cx,
 9377                &run.buffer_marked_text,
 9378                vec![run.completion_text],
 9379                counter.clone(),
 9380            )
 9381            .await;
 9382            cx.condition(|editor, _| editor.context_menu_visible())
 9383                .await;
 9384            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9385
 9386            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9387                editor
 9388                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9389                    .unwrap()
 9390            });
 9391            cx.assert_editor_state(&expected_text);
 9392            handle_resolve_completion_request(&mut cx, None).await;
 9393            apply_additional_edits.await.unwrap();
 9394        }
 9395    }
 9396}
 9397
 9398#[gpui::test]
 9399async fn test_completion(cx: &mut TestAppContext) {
 9400    init_test(cx, |_| {});
 9401
 9402    let mut cx = EditorLspTestContext::new_rust(
 9403        lsp::ServerCapabilities {
 9404            completion_provider: Some(lsp::CompletionOptions {
 9405                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9406                resolve_provider: Some(true),
 9407                ..Default::default()
 9408            }),
 9409            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9410            ..Default::default()
 9411        },
 9412        cx,
 9413    )
 9414    .await;
 9415    let counter = Arc::new(AtomicUsize::new(0));
 9416
 9417    cx.set_state(indoc! {"
 9418        oneˇ
 9419        two
 9420        three
 9421    "});
 9422    cx.simulate_keystroke(".");
 9423    handle_completion_request(
 9424        &mut cx,
 9425        indoc! {"
 9426            one.|<>
 9427            two
 9428            three
 9429        "},
 9430        vec!["first_completion", "second_completion"],
 9431        counter.clone(),
 9432    )
 9433    .await;
 9434    cx.condition(|editor, _| editor.context_menu_visible())
 9435        .await;
 9436    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9437
 9438    let _handler = handle_signature_help_request(
 9439        &mut cx,
 9440        lsp::SignatureHelp {
 9441            signatures: vec![lsp::SignatureInformation {
 9442                label: "test signature".to_string(),
 9443                documentation: None,
 9444                parameters: Some(vec![lsp::ParameterInformation {
 9445                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9446                    documentation: None,
 9447                }]),
 9448                active_parameter: None,
 9449            }],
 9450            active_signature: None,
 9451            active_parameter: None,
 9452        },
 9453    );
 9454    cx.update_editor(|editor, window, cx| {
 9455        assert!(
 9456            !editor.signature_help_state.is_shown(),
 9457            "No signature help was called for"
 9458        );
 9459        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9460    });
 9461    cx.run_until_parked();
 9462    cx.update_editor(|editor, _, _| {
 9463        assert!(
 9464            !editor.signature_help_state.is_shown(),
 9465            "No signature help should be shown when completions menu is open"
 9466        );
 9467    });
 9468
 9469    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9470        editor.context_menu_next(&Default::default(), window, cx);
 9471        editor
 9472            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9473            .unwrap()
 9474    });
 9475    cx.assert_editor_state(indoc! {"
 9476        one.second_completionˇ
 9477        two
 9478        three
 9479    "});
 9480
 9481    handle_resolve_completion_request(
 9482        &mut cx,
 9483        Some(vec![
 9484            (
 9485                //This overlaps with the primary completion edit which is
 9486                //misbehavior from the LSP spec, test that we filter it out
 9487                indoc! {"
 9488                    one.second_ˇcompletion
 9489                    two
 9490                    threeˇ
 9491                "},
 9492                "overlapping additional edit",
 9493            ),
 9494            (
 9495                indoc! {"
 9496                    one.second_completion
 9497                    two
 9498                    threeˇ
 9499                "},
 9500                "\nadditional edit",
 9501            ),
 9502        ]),
 9503    )
 9504    .await;
 9505    apply_additional_edits.await.unwrap();
 9506    cx.assert_editor_state(indoc! {"
 9507        one.second_completionˇ
 9508        two
 9509        three
 9510        additional edit
 9511    "});
 9512
 9513    cx.set_state(indoc! {"
 9514        one.second_completion
 9515        twoˇ
 9516        threeˇ
 9517        additional edit
 9518    "});
 9519    cx.simulate_keystroke(" ");
 9520    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9521    cx.simulate_keystroke("s");
 9522    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9523
 9524    cx.assert_editor_state(indoc! {"
 9525        one.second_completion
 9526        two sˇ
 9527        three sˇ
 9528        additional edit
 9529    "});
 9530    handle_completion_request(
 9531        &mut cx,
 9532        indoc! {"
 9533            one.second_completion
 9534            two s
 9535            three <s|>
 9536            additional edit
 9537        "},
 9538        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9539        counter.clone(),
 9540    )
 9541    .await;
 9542    cx.condition(|editor, _| editor.context_menu_visible())
 9543        .await;
 9544    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9545
 9546    cx.simulate_keystroke("i");
 9547
 9548    handle_completion_request(
 9549        &mut cx,
 9550        indoc! {"
 9551            one.second_completion
 9552            two si
 9553            three <si|>
 9554            additional edit
 9555        "},
 9556        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9557        counter.clone(),
 9558    )
 9559    .await;
 9560    cx.condition(|editor, _| editor.context_menu_visible())
 9561        .await;
 9562    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9563
 9564    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9565        editor
 9566            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9567            .unwrap()
 9568    });
 9569    cx.assert_editor_state(indoc! {"
 9570        one.second_completion
 9571        two sixth_completionˇ
 9572        three sixth_completionˇ
 9573        additional edit
 9574    "});
 9575
 9576    apply_additional_edits.await.unwrap();
 9577
 9578    update_test_language_settings(&mut cx, |settings| {
 9579        settings.defaults.show_completions_on_input = Some(false);
 9580    });
 9581    cx.set_state("editorˇ");
 9582    cx.simulate_keystroke(".");
 9583    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9584    cx.simulate_keystrokes("c l o");
 9585    cx.assert_editor_state("editor.cloˇ");
 9586    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9587    cx.update_editor(|editor, window, cx| {
 9588        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9589    });
 9590    handle_completion_request(
 9591        &mut cx,
 9592        "editor.<clo|>",
 9593        vec!["close", "clobber"],
 9594        counter.clone(),
 9595    )
 9596    .await;
 9597    cx.condition(|editor, _| editor.context_menu_visible())
 9598        .await;
 9599    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9600
 9601    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9602        editor
 9603            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9604            .unwrap()
 9605    });
 9606    cx.assert_editor_state("editor.closeˇ");
 9607    handle_resolve_completion_request(&mut cx, None).await;
 9608    apply_additional_edits.await.unwrap();
 9609}
 9610
 9611#[gpui::test]
 9612async fn test_word_completion(cx: &mut TestAppContext) {
 9613    let lsp_fetch_timeout_ms = 10;
 9614    init_test(cx, |language_settings| {
 9615        language_settings.defaults.completions = Some(CompletionSettings {
 9616            words: WordsCompletionMode::Fallback,
 9617            lsp: true,
 9618            lsp_fetch_timeout_ms: 10,
 9619            lsp_insert_mode: LspInsertMode::Insert,
 9620        });
 9621    });
 9622
 9623    let mut cx = EditorLspTestContext::new_rust(
 9624        lsp::ServerCapabilities {
 9625            completion_provider: Some(lsp::CompletionOptions {
 9626                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9627                ..lsp::CompletionOptions::default()
 9628            }),
 9629            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9630            ..lsp::ServerCapabilities::default()
 9631        },
 9632        cx,
 9633    )
 9634    .await;
 9635
 9636    let throttle_completions = Arc::new(AtomicBool::new(false));
 9637
 9638    let lsp_throttle_completions = throttle_completions.clone();
 9639    let _completion_requests_handler =
 9640        cx.lsp
 9641            .server
 9642            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9643                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9644                let cx = cx.clone();
 9645                async move {
 9646                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9647                        cx.background_executor()
 9648                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9649                            .await;
 9650                    }
 9651                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9652                        lsp::CompletionItem {
 9653                            label: "first".into(),
 9654                            ..lsp::CompletionItem::default()
 9655                        },
 9656                        lsp::CompletionItem {
 9657                            label: "last".into(),
 9658                            ..lsp::CompletionItem::default()
 9659                        },
 9660                    ])))
 9661                }
 9662            });
 9663
 9664    cx.set_state(indoc! {"
 9665        oneˇ
 9666        two
 9667        three
 9668    "});
 9669    cx.simulate_keystroke(".");
 9670    cx.executor().run_until_parked();
 9671    cx.condition(|editor, _| editor.context_menu_visible())
 9672        .await;
 9673    cx.update_editor(|editor, window, cx| {
 9674        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9675        {
 9676            assert_eq!(
 9677                completion_menu_entries(&menu),
 9678                &["first", "last"],
 9679                "When LSP server is fast to reply, no fallback word completions are used"
 9680            );
 9681        } else {
 9682            panic!("expected completion menu to be open");
 9683        }
 9684        editor.cancel(&Cancel, window, cx);
 9685    });
 9686    cx.executor().run_until_parked();
 9687    cx.condition(|editor, _| !editor.context_menu_visible())
 9688        .await;
 9689
 9690    throttle_completions.store(true, atomic::Ordering::Release);
 9691    cx.simulate_keystroke(".");
 9692    cx.executor()
 9693        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9694    cx.executor().run_until_parked();
 9695    cx.condition(|editor, _| editor.context_menu_visible())
 9696        .await;
 9697    cx.update_editor(|editor, _, _| {
 9698        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9699        {
 9700            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9701                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9702        } else {
 9703            panic!("expected completion menu to be open");
 9704        }
 9705    });
 9706}
 9707
 9708#[gpui::test]
 9709async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9710    init_test(cx, |language_settings| {
 9711        language_settings.defaults.completions = Some(CompletionSettings {
 9712            words: WordsCompletionMode::Enabled,
 9713            lsp: true,
 9714            lsp_fetch_timeout_ms: 0,
 9715            lsp_insert_mode: LspInsertMode::Insert,
 9716        });
 9717    });
 9718
 9719    let mut cx = EditorLspTestContext::new_rust(
 9720        lsp::ServerCapabilities {
 9721            completion_provider: Some(lsp::CompletionOptions {
 9722                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9723                ..lsp::CompletionOptions::default()
 9724            }),
 9725            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9726            ..lsp::ServerCapabilities::default()
 9727        },
 9728        cx,
 9729    )
 9730    .await;
 9731
 9732    let _completion_requests_handler =
 9733        cx.lsp
 9734            .server
 9735            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9736                Ok(Some(lsp::CompletionResponse::Array(vec![
 9737                    lsp::CompletionItem {
 9738                        label: "first".into(),
 9739                        ..lsp::CompletionItem::default()
 9740                    },
 9741                    lsp::CompletionItem {
 9742                        label: "last".into(),
 9743                        ..lsp::CompletionItem::default()
 9744                    },
 9745                ])))
 9746            });
 9747
 9748    cx.set_state(indoc! {"ˇ
 9749        first
 9750        last
 9751        second
 9752    "});
 9753    cx.simulate_keystroke(".");
 9754    cx.executor().run_until_parked();
 9755    cx.condition(|editor, _| editor.context_menu_visible())
 9756        .await;
 9757    cx.update_editor(|editor, _, _| {
 9758        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9759        {
 9760            assert_eq!(
 9761                completion_menu_entries(&menu),
 9762                &["first", "last", "second"],
 9763                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9764            );
 9765        } else {
 9766            panic!("expected completion menu to be open");
 9767        }
 9768    });
 9769}
 9770
 9771#[gpui::test]
 9772async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9773    init_test(cx, |language_settings| {
 9774        language_settings.defaults.completions = Some(CompletionSettings {
 9775            words: WordsCompletionMode::Disabled,
 9776            lsp: true,
 9777            lsp_fetch_timeout_ms: 0,
 9778            lsp_insert_mode: LspInsertMode::Insert,
 9779        });
 9780    });
 9781
 9782    let mut cx = EditorLspTestContext::new_rust(
 9783        lsp::ServerCapabilities {
 9784            completion_provider: Some(lsp::CompletionOptions {
 9785                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9786                ..lsp::CompletionOptions::default()
 9787            }),
 9788            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9789            ..lsp::ServerCapabilities::default()
 9790        },
 9791        cx,
 9792    )
 9793    .await;
 9794
 9795    let _completion_requests_handler =
 9796        cx.lsp
 9797            .server
 9798            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9799                panic!("LSP completions should not be queried when dealing with word completions")
 9800            });
 9801
 9802    cx.set_state(indoc! {"ˇ
 9803        first
 9804        last
 9805        second
 9806    "});
 9807    cx.update_editor(|editor, window, cx| {
 9808        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9809    });
 9810    cx.executor().run_until_parked();
 9811    cx.condition(|editor, _| editor.context_menu_visible())
 9812        .await;
 9813    cx.update_editor(|editor, _, _| {
 9814        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9815        {
 9816            assert_eq!(
 9817                completion_menu_entries(&menu),
 9818                &["first", "last", "second"],
 9819                "`ShowWordCompletions` action should show word completions"
 9820            );
 9821        } else {
 9822            panic!("expected completion menu to be open");
 9823        }
 9824    });
 9825
 9826    cx.simulate_keystroke("l");
 9827    cx.executor().run_until_parked();
 9828    cx.condition(|editor, _| editor.context_menu_visible())
 9829        .await;
 9830    cx.update_editor(|editor, _, _| {
 9831        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9832        {
 9833            assert_eq!(
 9834                completion_menu_entries(&menu),
 9835                &["last"],
 9836                "After showing word completions, further editing should filter them and not query the LSP"
 9837            );
 9838        } else {
 9839            panic!("expected completion menu to be open");
 9840        }
 9841    });
 9842}
 9843
 9844#[gpui::test]
 9845async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9846    init_test(cx, |language_settings| {
 9847        language_settings.defaults.completions = Some(CompletionSettings {
 9848            words: WordsCompletionMode::Fallback,
 9849            lsp: false,
 9850            lsp_fetch_timeout_ms: 0,
 9851            lsp_insert_mode: LspInsertMode::Insert,
 9852        });
 9853    });
 9854
 9855    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9856
 9857    cx.set_state(indoc! {"ˇ
 9858        0_usize
 9859        let
 9860        33
 9861        4.5f32
 9862    "});
 9863    cx.update_editor(|editor, window, cx| {
 9864        editor.show_completions(&ShowCompletions::default(), window, cx);
 9865    });
 9866    cx.executor().run_until_parked();
 9867    cx.condition(|editor, _| editor.context_menu_visible())
 9868        .await;
 9869    cx.update_editor(|editor, window, cx| {
 9870        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9871        {
 9872            assert_eq!(
 9873                completion_menu_entries(&menu),
 9874                &["let"],
 9875                "With no digits in the completion query, no digits should be in the word completions"
 9876            );
 9877        } else {
 9878            panic!("expected completion menu to be open");
 9879        }
 9880        editor.cancel(&Cancel, window, cx);
 9881    });
 9882
 9883    cx.set_state(indoc! {" 9884        0_usize
 9885        let
 9886        3
 9887        33.35f32
 9888    "});
 9889    cx.update_editor(|editor, window, cx| {
 9890        editor.show_completions(&ShowCompletions::default(), window, cx);
 9891    });
 9892    cx.executor().run_until_parked();
 9893    cx.condition(|editor, _| editor.context_menu_visible())
 9894        .await;
 9895    cx.update_editor(|editor, _, _| {
 9896        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9897        {
 9898            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9899                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9900        } else {
 9901            panic!("expected completion menu to be open");
 9902        }
 9903    });
 9904}
 9905
 9906fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9907    let position = || lsp::Position {
 9908        line: params.text_document_position.position.line,
 9909        character: params.text_document_position.position.character,
 9910    };
 9911    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9912        range: lsp::Range {
 9913            start: position(),
 9914            end: position(),
 9915        },
 9916        new_text: text.to_string(),
 9917    }))
 9918}
 9919
 9920#[gpui::test]
 9921async fn test_multiline_completion(cx: &mut TestAppContext) {
 9922    init_test(cx, |_| {});
 9923
 9924    let fs = FakeFs::new(cx.executor());
 9925    fs.insert_tree(
 9926        path!("/a"),
 9927        json!({
 9928            "main.ts": "a",
 9929        }),
 9930    )
 9931    .await;
 9932
 9933    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9934    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9935    let typescript_language = Arc::new(Language::new(
 9936        LanguageConfig {
 9937            name: "TypeScript".into(),
 9938            matcher: LanguageMatcher {
 9939                path_suffixes: vec!["ts".to_string()],
 9940                ..LanguageMatcher::default()
 9941            },
 9942            line_comments: vec!["// ".into()],
 9943            ..LanguageConfig::default()
 9944        },
 9945        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9946    ));
 9947    language_registry.add(typescript_language.clone());
 9948    let mut fake_servers = language_registry.register_fake_lsp(
 9949        "TypeScript",
 9950        FakeLspAdapter {
 9951            capabilities: lsp::ServerCapabilities {
 9952                completion_provider: Some(lsp::CompletionOptions {
 9953                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9954                    ..lsp::CompletionOptions::default()
 9955                }),
 9956                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9957                ..lsp::ServerCapabilities::default()
 9958            },
 9959            // Emulate vtsls label generation
 9960            label_for_completion: Some(Box::new(|item, _| {
 9961                let text = if let Some(description) = item
 9962                    .label_details
 9963                    .as_ref()
 9964                    .and_then(|label_details| label_details.description.as_ref())
 9965                {
 9966                    format!("{} {}", item.label, description)
 9967                } else if let Some(detail) = &item.detail {
 9968                    format!("{} {}", item.label, detail)
 9969                } else {
 9970                    item.label.clone()
 9971                };
 9972                let len = text.len();
 9973                Some(language::CodeLabel {
 9974                    text,
 9975                    runs: Vec::new(),
 9976                    filter_range: 0..len,
 9977                })
 9978            })),
 9979            ..FakeLspAdapter::default()
 9980        },
 9981    );
 9982    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9983    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9984    let worktree_id = workspace
 9985        .update(cx, |workspace, _window, cx| {
 9986            workspace.project().update(cx, |project, cx| {
 9987                project.worktrees(cx).next().unwrap().read(cx).id()
 9988            })
 9989        })
 9990        .unwrap();
 9991    let _buffer = project
 9992        .update(cx, |project, cx| {
 9993            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9994        })
 9995        .await
 9996        .unwrap();
 9997    let editor = workspace
 9998        .update(cx, |workspace, window, cx| {
 9999            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10000        })
10001        .unwrap()
10002        .await
10003        .unwrap()
10004        .downcast::<Editor>()
10005        .unwrap();
10006    let fake_server = fake_servers.next().await.unwrap();
10007
10008    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10009    let multiline_label_2 = "a\nb\nc\n";
10010    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10011    let multiline_description = "d\ne\nf\n";
10012    let multiline_detail_2 = "g\nh\ni\n";
10013
10014    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10015        move |params, _| async move {
10016            Ok(Some(lsp::CompletionResponse::Array(vec![
10017                lsp::CompletionItem {
10018                    label: multiline_label.to_string(),
10019                    text_edit: gen_text_edit(&params, "new_text_1"),
10020                    ..lsp::CompletionItem::default()
10021                },
10022                lsp::CompletionItem {
10023                    label: "single line label 1".to_string(),
10024                    detail: Some(multiline_detail.to_string()),
10025                    text_edit: gen_text_edit(&params, "new_text_2"),
10026                    ..lsp::CompletionItem::default()
10027                },
10028                lsp::CompletionItem {
10029                    label: "single line label 2".to_string(),
10030                    label_details: Some(lsp::CompletionItemLabelDetails {
10031                        description: Some(multiline_description.to_string()),
10032                        detail: None,
10033                    }),
10034                    text_edit: gen_text_edit(&params, "new_text_2"),
10035                    ..lsp::CompletionItem::default()
10036                },
10037                lsp::CompletionItem {
10038                    label: multiline_label_2.to_string(),
10039                    detail: Some(multiline_detail_2.to_string()),
10040                    text_edit: gen_text_edit(&params, "new_text_3"),
10041                    ..lsp::CompletionItem::default()
10042                },
10043                lsp::CompletionItem {
10044                    label: "Label with many     spaces and \t but without newlines".to_string(),
10045                    detail: Some(
10046                        "Details with many     spaces and \t but without newlines".to_string(),
10047                    ),
10048                    text_edit: gen_text_edit(&params, "new_text_4"),
10049                    ..lsp::CompletionItem::default()
10050                },
10051            ])))
10052        },
10053    );
10054
10055    editor.update_in(cx, |editor, window, cx| {
10056        cx.focus_self(window);
10057        editor.move_to_end(&MoveToEnd, window, cx);
10058        editor.handle_input(".", window, cx);
10059    });
10060    cx.run_until_parked();
10061    completion_handle.next().await.unwrap();
10062
10063    editor.update(cx, |editor, _| {
10064        assert!(editor.context_menu_visible());
10065        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10066        {
10067            let completion_labels = menu
10068                .completions
10069                .borrow()
10070                .iter()
10071                .map(|c| c.label.text.clone())
10072                .collect::<Vec<_>>();
10073            assert_eq!(
10074                completion_labels,
10075                &[
10076                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10077                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10078                    "single line label 2 d e f ",
10079                    "a b c g h i ",
10080                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10081                ],
10082                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10083            );
10084
10085            for completion in menu
10086                .completions
10087                .borrow()
10088                .iter() {
10089                    assert_eq!(
10090                        completion.label.filter_range,
10091                        0..completion.label.text.len(),
10092                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10093                    );
10094                }
10095        } else {
10096            panic!("expected completion menu to be open");
10097        }
10098    });
10099}
10100
10101#[gpui::test]
10102async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10103    init_test(cx, |_| {});
10104    let mut cx = EditorLspTestContext::new_rust(
10105        lsp::ServerCapabilities {
10106            completion_provider: Some(lsp::CompletionOptions {
10107                trigger_characters: Some(vec![".".to_string()]),
10108                ..Default::default()
10109            }),
10110            ..Default::default()
10111        },
10112        cx,
10113    )
10114    .await;
10115    cx.lsp
10116        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10117            Ok(Some(lsp::CompletionResponse::Array(vec![
10118                lsp::CompletionItem {
10119                    label: "first".into(),
10120                    ..Default::default()
10121                },
10122                lsp::CompletionItem {
10123                    label: "last".into(),
10124                    ..Default::default()
10125                },
10126            ])))
10127        });
10128    cx.set_state("variableˇ");
10129    cx.simulate_keystroke(".");
10130    cx.executor().run_until_parked();
10131
10132    cx.update_editor(|editor, _, _| {
10133        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10134        {
10135            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
10136        } else {
10137            panic!("expected completion menu to be open");
10138        }
10139    });
10140
10141    cx.update_editor(|editor, window, cx| {
10142        editor.move_page_down(&MovePageDown::default(), window, cx);
10143        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10144        {
10145            assert!(
10146                menu.selected_item == 1,
10147                "expected PageDown to select the last item from the context menu"
10148            );
10149        } else {
10150            panic!("expected completion menu to stay open after PageDown");
10151        }
10152    });
10153
10154    cx.update_editor(|editor, window, cx| {
10155        editor.move_page_up(&MovePageUp::default(), window, cx);
10156        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10157        {
10158            assert!(
10159                menu.selected_item == 0,
10160                "expected PageUp to select the first item from the context menu"
10161            );
10162        } else {
10163            panic!("expected completion menu to stay open after PageUp");
10164        }
10165    });
10166}
10167
10168#[gpui::test]
10169async fn test_completion_sort(cx: &mut TestAppContext) {
10170    init_test(cx, |_| {});
10171    let mut cx = EditorLspTestContext::new_rust(
10172        lsp::ServerCapabilities {
10173            completion_provider: Some(lsp::CompletionOptions {
10174                trigger_characters: Some(vec![".".to_string()]),
10175                ..Default::default()
10176            }),
10177            ..Default::default()
10178        },
10179        cx,
10180    )
10181    .await;
10182    cx.lsp
10183        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10184            Ok(Some(lsp::CompletionResponse::Array(vec![
10185                lsp::CompletionItem {
10186                    label: "Range".into(),
10187                    sort_text: Some("a".into()),
10188                    ..Default::default()
10189                },
10190                lsp::CompletionItem {
10191                    label: "r".into(),
10192                    sort_text: Some("b".into()),
10193                    ..Default::default()
10194                },
10195                lsp::CompletionItem {
10196                    label: "ret".into(),
10197                    sort_text: Some("c".into()),
10198                    ..Default::default()
10199                },
10200                lsp::CompletionItem {
10201                    label: "return".into(),
10202                    sort_text: Some("d".into()),
10203                    ..Default::default()
10204                },
10205                lsp::CompletionItem {
10206                    label: "slice".into(),
10207                    sort_text: Some("d".into()),
10208                    ..Default::default()
10209                },
10210            ])))
10211        });
10212    cx.set_state("");
10213    cx.executor().run_until_parked();
10214    cx.update_editor(|editor, window, cx| {
10215        editor.show_completions(
10216            &ShowCompletions {
10217                trigger: Some("r".into()),
10218            },
10219            window,
10220            cx,
10221        );
10222    });
10223    cx.executor().run_until_parked();
10224
10225    cx.update_editor(|editor, _, _| {
10226        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10227        {
10228            assert_eq!(
10229                completion_menu_entries(&menu),
10230                &["r", "ret", "Range", "return"]
10231            );
10232        } else {
10233            panic!("expected completion menu to be open");
10234        }
10235    });
10236}
10237
10238#[gpui::test]
10239async fn test_as_is_completions(cx: &mut TestAppContext) {
10240    init_test(cx, |_| {});
10241    let mut cx = EditorLspTestContext::new_rust(
10242        lsp::ServerCapabilities {
10243            completion_provider: Some(lsp::CompletionOptions {
10244                ..Default::default()
10245            }),
10246            ..Default::default()
10247        },
10248        cx,
10249    )
10250    .await;
10251    cx.lsp
10252        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10253            Ok(Some(lsp::CompletionResponse::Array(vec![
10254                lsp::CompletionItem {
10255                    label: "unsafe".into(),
10256                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10257                        range: lsp::Range {
10258                            start: lsp::Position {
10259                                line: 1,
10260                                character: 2,
10261                            },
10262                            end: lsp::Position {
10263                                line: 1,
10264                                character: 3,
10265                            },
10266                        },
10267                        new_text: "unsafe".to_string(),
10268                    })),
10269                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
10270                    ..Default::default()
10271                },
10272            ])))
10273        });
10274    cx.set_state("fn a() {}\n");
10275    cx.executor().run_until_parked();
10276    cx.update_editor(|editor, window, cx| {
10277        editor.show_completions(
10278            &ShowCompletions {
10279                trigger: Some("\n".into()),
10280            },
10281            window,
10282            cx,
10283        );
10284    });
10285    cx.executor().run_until_parked();
10286
10287    cx.update_editor(|editor, window, cx| {
10288        editor.confirm_completion(&Default::default(), window, cx)
10289    });
10290    cx.executor().run_until_parked();
10291    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
10292}
10293
10294#[gpui::test]
10295async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10296    init_test(cx, |_| {});
10297
10298    let mut cx = EditorLspTestContext::new_rust(
10299        lsp::ServerCapabilities {
10300            completion_provider: Some(lsp::CompletionOptions {
10301                trigger_characters: Some(vec![".".to_string()]),
10302                resolve_provider: Some(true),
10303                ..Default::default()
10304            }),
10305            ..Default::default()
10306        },
10307        cx,
10308    )
10309    .await;
10310
10311    cx.set_state("fn main() { let a = 2ˇ; }");
10312    cx.simulate_keystroke(".");
10313    let completion_item = lsp::CompletionItem {
10314        label: "Some".into(),
10315        kind: Some(lsp::CompletionItemKind::SNIPPET),
10316        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10317        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10318            kind: lsp::MarkupKind::Markdown,
10319            value: "```rust\nSome(2)\n```".to_string(),
10320        })),
10321        deprecated: Some(false),
10322        sort_text: Some("Some".to_string()),
10323        filter_text: Some("Some".to_string()),
10324        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10325        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10326            range: lsp::Range {
10327                start: lsp::Position {
10328                    line: 0,
10329                    character: 22,
10330                },
10331                end: lsp::Position {
10332                    line: 0,
10333                    character: 22,
10334                },
10335            },
10336            new_text: "Some(2)".to_string(),
10337        })),
10338        additional_text_edits: Some(vec![lsp::TextEdit {
10339            range: lsp::Range {
10340                start: lsp::Position {
10341                    line: 0,
10342                    character: 20,
10343                },
10344                end: lsp::Position {
10345                    line: 0,
10346                    character: 22,
10347                },
10348            },
10349            new_text: "".to_string(),
10350        }]),
10351        ..Default::default()
10352    };
10353
10354    let closure_completion_item = completion_item.clone();
10355    let counter = Arc::new(AtomicUsize::new(0));
10356    let counter_clone = counter.clone();
10357    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10358        let task_completion_item = closure_completion_item.clone();
10359        counter_clone.fetch_add(1, atomic::Ordering::Release);
10360        async move {
10361            Ok(Some(lsp::CompletionResponse::Array(vec![
10362                task_completion_item,
10363            ])))
10364        }
10365    });
10366
10367    cx.condition(|editor, _| editor.context_menu_visible())
10368        .await;
10369    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10370    assert!(request.next().await.is_some());
10371    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10372
10373    cx.simulate_keystrokes("S o m");
10374    cx.condition(|editor, _| editor.context_menu_visible())
10375        .await;
10376    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10377    assert!(request.next().await.is_some());
10378    assert!(request.next().await.is_some());
10379    assert!(request.next().await.is_some());
10380    request.close();
10381    assert!(request.next().await.is_none());
10382    assert_eq!(
10383        counter.load(atomic::Ordering::Acquire),
10384        4,
10385        "With the completions menu open, only one LSP request should happen per input"
10386    );
10387}
10388
10389#[gpui::test]
10390async fn test_toggle_comment(cx: &mut TestAppContext) {
10391    init_test(cx, |_| {});
10392    let mut cx = EditorTestContext::new(cx).await;
10393    let language = Arc::new(Language::new(
10394        LanguageConfig {
10395            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10396            ..Default::default()
10397        },
10398        Some(tree_sitter_rust::LANGUAGE.into()),
10399    ));
10400    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10401
10402    // If multiple selections intersect a line, the line is only toggled once.
10403    cx.set_state(indoc! {"
10404        fn a() {
10405            «//b();
10406            ˇ»// «c();
10407            //ˇ»  d();
10408        }
10409    "});
10410
10411    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10412
10413    cx.assert_editor_state(indoc! {"
10414        fn a() {
10415            «b();
10416            c();
10417            ˇ» d();
10418        }
10419    "});
10420
10421    // The comment prefix is inserted at the same column for every line in a
10422    // selection.
10423    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10424
10425    cx.assert_editor_state(indoc! {"
10426        fn a() {
10427            // «b();
10428            // c();
10429            ˇ»//  d();
10430        }
10431    "});
10432
10433    // If a selection ends at the beginning of a line, that line is not toggled.
10434    cx.set_selections_state(indoc! {"
10435        fn a() {
10436            // b();
10437            «// c();
10438        ˇ»    //  d();
10439        }
10440    "});
10441
10442    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10443
10444    cx.assert_editor_state(indoc! {"
10445        fn a() {
10446            // b();
10447            «c();
10448        ˇ»    //  d();
10449        }
10450    "});
10451
10452    // If a selection span a single line and is empty, the line is toggled.
10453    cx.set_state(indoc! {"
10454        fn a() {
10455            a();
10456            b();
10457        ˇ
10458        }
10459    "});
10460
10461    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10462
10463    cx.assert_editor_state(indoc! {"
10464        fn a() {
10465            a();
10466            b();
10467        //•ˇ
10468        }
10469    "});
10470
10471    // If a selection span multiple lines, empty lines are not toggled.
10472    cx.set_state(indoc! {"
10473        fn a() {
10474            «a();
10475
10476            c();ˇ»
10477        }
10478    "});
10479
10480    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10481
10482    cx.assert_editor_state(indoc! {"
10483        fn a() {
10484            // «a();
10485
10486            // c();ˇ»
10487        }
10488    "});
10489
10490    // If a selection includes multiple comment prefixes, all lines are uncommented.
10491    cx.set_state(indoc! {"
10492        fn a() {
10493            «// a();
10494            /// b();
10495            //! c();ˇ»
10496        }
10497    "});
10498
10499    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10500
10501    cx.assert_editor_state(indoc! {"
10502        fn a() {
10503            «a();
10504            b();
10505            c();ˇ»
10506        }
10507    "});
10508}
10509
10510#[gpui::test]
10511async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10512    init_test(cx, |_| {});
10513    let mut cx = EditorTestContext::new(cx).await;
10514    let language = Arc::new(Language::new(
10515        LanguageConfig {
10516            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10517            ..Default::default()
10518        },
10519        Some(tree_sitter_rust::LANGUAGE.into()),
10520    ));
10521    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10522
10523    let toggle_comments = &ToggleComments {
10524        advance_downwards: false,
10525        ignore_indent: true,
10526    };
10527
10528    // If multiple selections intersect a line, the line is only toggled once.
10529    cx.set_state(indoc! {"
10530        fn a() {
10531        //    «b();
10532        //    c();
10533        //    ˇ» d();
10534        }
10535    "});
10536
10537    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10538
10539    cx.assert_editor_state(indoc! {"
10540        fn a() {
10541            «b();
10542            c();
10543            ˇ» d();
10544        }
10545    "});
10546
10547    // The comment prefix is inserted at the beginning of each line
10548    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10549
10550    cx.assert_editor_state(indoc! {"
10551        fn a() {
10552        //    «b();
10553        //    c();
10554        //    ˇ» d();
10555        }
10556    "});
10557
10558    // If a selection ends at the beginning of a line, that line is not toggled.
10559    cx.set_selections_state(indoc! {"
10560        fn a() {
10561        //    b();
10562        //    «c();
10563        ˇ»//     d();
10564        }
10565    "});
10566
10567    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10568
10569    cx.assert_editor_state(indoc! {"
10570        fn a() {
10571        //    b();
10572            «c();
10573        ˇ»//     d();
10574        }
10575    "});
10576
10577    // If a selection span a single line and is empty, the line is toggled.
10578    cx.set_state(indoc! {"
10579        fn a() {
10580            a();
10581            b();
10582        ˇ
10583        }
10584    "});
10585
10586    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10587
10588    cx.assert_editor_state(indoc! {"
10589        fn a() {
10590            a();
10591            b();
10592        //ˇ
10593        }
10594    "});
10595
10596    // If a selection span multiple lines, empty lines are not toggled.
10597    cx.set_state(indoc! {"
10598        fn a() {
10599            «a();
10600
10601            c();ˇ»
10602        }
10603    "});
10604
10605    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10606
10607    cx.assert_editor_state(indoc! {"
10608        fn a() {
10609        //    «a();
10610
10611        //    c();ˇ»
10612        }
10613    "});
10614
10615    // If a selection includes multiple comment prefixes, all lines are uncommented.
10616    cx.set_state(indoc! {"
10617        fn a() {
10618        //    «a();
10619        ///    b();
10620        //!    c();ˇ»
10621        }
10622    "});
10623
10624    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10625
10626    cx.assert_editor_state(indoc! {"
10627        fn a() {
10628            «a();
10629            b();
10630            c();ˇ»
10631        }
10632    "});
10633}
10634
10635#[gpui::test]
10636async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10637    init_test(cx, |_| {});
10638
10639    let language = Arc::new(Language::new(
10640        LanguageConfig {
10641            line_comments: vec!["// ".into()],
10642            ..Default::default()
10643        },
10644        Some(tree_sitter_rust::LANGUAGE.into()),
10645    ));
10646
10647    let mut cx = EditorTestContext::new(cx).await;
10648
10649    cx.language_registry().add(language.clone());
10650    cx.update_buffer(|buffer, cx| {
10651        buffer.set_language(Some(language), cx);
10652    });
10653
10654    let toggle_comments = &ToggleComments {
10655        advance_downwards: true,
10656        ignore_indent: false,
10657    };
10658
10659    // Single cursor on one line -> advance
10660    // Cursor moves horizontally 3 characters as well on non-blank line
10661    cx.set_state(indoc!(
10662        "fn a() {
10663             ˇdog();
10664             cat();
10665        }"
10666    ));
10667    cx.update_editor(|editor, window, cx| {
10668        editor.toggle_comments(toggle_comments, window, cx);
10669    });
10670    cx.assert_editor_state(indoc!(
10671        "fn a() {
10672             // dog();
10673             catˇ();
10674        }"
10675    ));
10676
10677    // Single selection on one line -> don't advance
10678    cx.set_state(indoc!(
10679        "fn a() {
10680             «dog()ˇ»;
10681             cat();
10682        }"
10683    ));
10684    cx.update_editor(|editor, window, cx| {
10685        editor.toggle_comments(toggle_comments, window, cx);
10686    });
10687    cx.assert_editor_state(indoc!(
10688        "fn a() {
10689             // «dog()ˇ»;
10690             cat();
10691        }"
10692    ));
10693
10694    // Multiple cursors on one line -> advance
10695    cx.set_state(indoc!(
10696        "fn a() {
10697             ˇdˇog();
10698             cat();
10699        }"
10700    ));
10701    cx.update_editor(|editor, window, cx| {
10702        editor.toggle_comments(toggle_comments, window, cx);
10703    });
10704    cx.assert_editor_state(indoc!(
10705        "fn a() {
10706             // dog();
10707             catˇ(ˇ);
10708        }"
10709    ));
10710
10711    // Multiple cursors on one line, with selection -> don't advance
10712    cx.set_state(indoc!(
10713        "fn a() {
10714             ˇdˇog«()ˇ»;
10715             cat();
10716        }"
10717    ));
10718    cx.update_editor(|editor, window, cx| {
10719        editor.toggle_comments(toggle_comments, window, cx);
10720    });
10721    cx.assert_editor_state(indoc!(
10722        "fn a() {
10723             // ˇdˇog«()ˇ»;
10724             cat();
10725        }"
10726    ));
10727
10728    // Single cursor on one line -> advance
10729    // Cursor moves to column 0 on blank line
10730    cx.set_state(indoc!(
10731        "fn a() {
10732             ˇdog();
10733
10734             cat();
10735        }"
10736    ));
10737    cx.update_editor(|editor, window, cx| {
10738        editor.toggle_comments(toggle_comments, window, cx);
10739    });
10740    cx.assert_editor_state(indoc!(
10741        "fn a() {
10742             // dog();
10743        ˇ
10744             cat();
10745        }"
10746    ));
10747
10748    // Single cursor on one line -> advance
10749    // Cursor starts and ends at column 0
10750    cx.set_state(indoc!(
10751        "fn a() {
10752         ˇ    dog();
10753             cat();
10754        }"
10755    ));
10756    cx.update_editor(|editor, window, cx| {
10757        editor.toggle_comments(toggle_comments, window, cx);
10758    });
10759    cx.assert_editor_state(indoc!(
10760        "fn a() {
10761             // dog();
10762         ˇ    cat();
10763        }"
10764    ));
10765}
10766
10767#[gpui::test]
10768async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10769    init_test(cx, |_| {});
10770
10771    let mut cx = EditorTestContext::new(cx).await;
10772
10773    let html_language = Arc::new(
10774        Language::new(
10775            LanguageConfig {
10776                name: "HTML".into(),
10777                block_comment: Some(("<!-- ".into(), " -->".into())),
10778                ..Default::default()
10779            },
10780            Some(tree_sitter_html::LANGUAGE.into()),
10781        )
10782        .with_injection_query(
10783            r#"
10784            (script_element
10785                (raw_text) @injection.content
10786                (#set! injection.language "javascript"))
10787            "#,
10788        )
10789        .unwrap(),
10790    );
10791
10792    let javascript_language = Arc::new(Language::new(
10793        LanguageConfig {
10794            name: "JavaScript".into(),
10795            line_comments: vec!["// ".into()],
10796            ..Default::default()
10797        },
10798        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10799    ));
10800
10801    cx.language_registry().add(html_language.clone());
10802    cx.language_registry().add(javascript_language.clone());
10803    cx.update_buffer(|buffer, cx| {
10804        buffer.set_language(Some(html_language), cx);
10805    });
10806
10807    // Toggle comments for empty selections
10808    cx.set_state(
10809        &r#"
10810            <p>A</p>ˇ
10811            <p>B</p>ˇ
10812            <p>C</p>ˇ
10813        "#
10814        .unindent(),
10815    );
10816    cx.update_editor(|editor, window, cx| {
10817        editor.toggle_comments(&ToggleComments::default(), window, cx)
10818    });
10819    cx.assert_editor_state(
10820        &r#"
10821            <!-- <p>A</p>ˇ -->
10822            <!-- <p>B</p>ˇ -->
10823            <!-- <p>C</p>ˇ -->
10824        "#
10825        .unindent(),
10826    );
10827    cx.update_editor(|editor, window, cx| {
10828        editor.toggle_comments(&ToggleComments::default(), window, cx)
10829    });
10830    cx.assert_editor_state(
10831        &r#"
10832            <p>A</p>ˇ
10833            <p>B</p>ˇ
10834            <p>C</p>ˇ
10835        "#
10836        .unindent(),
10837    );
10838
10839    // Toggle comments for mixture of empty and non-empty selections, where
10840    // multiple selections occupy a given line.
10841    cx.set_state(
10842        &r#"
10843            <p>A«</p>
10844            <p>ˇ»B</p>ˇ
10845            <p>C«</p>
10846            <p>ˇ»D</p>ˇ
10847        "#
10848        .unindent(),
10849    );
10850
10851    cx.update_editor(|editor, window, cx| {
10852        editor.toggle_comments(&ToggleComments::default(), window, cx)
10853    });
10854    cx.assert_editor_state(
10855        &r#"
10856            <!-- <p>A«</p>
10857            <p>ˇ»B</p>ˇ -->
10858            <!-- <p>C«</p>
10859            <p>ˇ»D</p>ˇ -->
10860        "#
10861        .unindent(),
10862    );
10863    cx.update_editor(|editor, window, cx| {
10864        editor.toggle_comments(&ToggleComments::default(), window, cx)
10865    });
10866    cx.assert_editor_state(
10867        &r#"
10868            <p>A«</p>
10869            <p>ˇ»B</p>ˇ
10870            <p>C«</p>
10871            <p>ˇ»D</p>ˇ
10872        "#
10873        .unindent(),
10874    );
10875
10876    // Toggle comments when different languages are active for different
10877    // selections.
10878    cx.set_state(
10879        &r#"
10880            ˇ<script>
10881                ˇvar x = new Y();
10882            ˇ</script>
10883        "#
10884        .unindent(),
10885    );
10886    cx.executor().run_until_parked();
10887    cx.update_editor(|editor, window, cx| {
10888        editor.toggle_comments(&ToggleComments::default(), window, cx)
10889    });
10890    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10891    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10892    cx.assert_editor_state(
10893        &r#"
10894            <!-- ˇ<script> -->
10895                // ˇvar x = new Y();
10896            <!-- ˇ</script> -->
10897        "#
10898        .unindent(),
10899    );
10900}
10901
10902#[gpui::test]
10903fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10904    init_test(cx, |_| {});
10905
10906    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10907    let multibuffer = cx.new(|cx| {
10908        let mut multibuffer = MultiBuffer::new(ReadWrite);
10909        multibuffer.push_excerpts(
10910            buffer.clone(),
10911            [
10912                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
10913                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
10914            ],
10915            cx,
10916        );
10917        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10918        multibuffer
10919    });
10920
10921    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10922    editor.update_in(cx, |editor, window, cx| {
10923        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10924        editor.change_selections(None, window, cx, |s| {
10925            s.select_ranges([
10926                Point::new(0, 0)..Point::new(0, 0),
10927                Point::new(1, 0)..Point::new(1, 0),
10928            ])
10929        });
10930
10931        editor.handle_input("X", window, cx);
10932        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10933        assert_eq!(
10934            editor.selections.ranges(cx),
10935            [
10936                Point::new(0, 1)..Point::new(0, 1),
10937                Point::new(1, 1)..Point::new(1, 1),
10938            ]
10939        );
10940
10941        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10942        editor.change_selections(None, window, cx, |s| {
10943            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10944        });
10945        editor.backspace(&Default::default(), window, cx);
10946        assert_eq!(editor.text(cx), "Xa\nbbb");
10947        assert_eq!(
10948            editor.selections.ranges(cx),
10949            [Point::new(1, 0)..Point::new(1, 0)]
10950        );
10951
10952        editor.change_selections(None, window, cx, |s| {
10953            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10954        });
10955        editor.backspace(&Default::default(), window, cx);
10956        assert_eq!(editor.text(cx), "X\nbb");
10957        assert_eq!(
10958            editor.selections.ranges(cx),
10959            [Point::new(0, 1)..Point::new(0, 1)]
10960        );
10961    });
10962}
10963
10964#[gpui::test]
10965fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10966    init_test(cx, |_| {});
10967
10968    let markers = vec![('[', ']').into(), ('(', ')').into()];
10969    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10970        indoc! {"
10971            [aaaa
10972            (bbbb]
10973            cccc)",
10974        },
10975        markers.clone(),
10976    );
10977    let excerpt_ranges = markers.into_iter().map(|marker| {
10978        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10979        ExcerptRange::new(context.clone())
10980    });
10981    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10982    let multibuffer = cx.new(|cx| {
10983        let mut multibuffer = MultiBuffer::new(ReadWrite);
10984        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10985        multibuffer
10986    });
10987
10988    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10989    editor.update_in(cx, |editor, window, cx| {
10990        let (expected_text, selection_ranges) = marked_text_ranges(
10991            indoc! {"
10992                aaaa
10993                bˇbbb
10994                bˇbbˇb
10995                cccc"
10996            },
10997            true,
10998        );
10999        assert_eq!(editor.text(cx), expected_text);
11000        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11001
11002        editor.handle_input("X", window, cx);
11003
11004        let (expected_text, expected_selections) = marked_text_ranges(
11005            indoc! {"
11006                aaaa
11007                bXˇbbXb
11008                bXˇbbXˇb
11009                cccc"
11010            },
11011            false,
11012        );
11013        assert_eq!(editor.text(cx), expected_text);
11014        assert_eq!(editor.selections.ranges(cx), expected_selections);
11015
11016        editor.newline(&Newline, window, cx);
11017        let (expected_text, expected_selections) = marked_text_ranges(
11018            indoc! {"
11019                aaaa
11020                bX
11021                ˇbbX
11022                b
11023                bX
11024                ˇbbX
11025                ˇb
11026                cccc"
11027            },
11028            false,
11029        );
11030        assert_eq!(editor.text(cx), expected_text);
11031        assert_eq!(editor.selections.ranges(cx), expected_selections);
11032    });
11033}
11034
11035#[gpui::test]
11036fn test_refresh_selections(cx: &mut TestAppContext) {
11037    init_test(cx, |_| {});
11038
11039    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11040    let mut excerpt1_id = None;
11041    let multibuffer = cx.new(|cx| {
11042        let mut multibuffer = MultiBuffer::new(ReadWrite);
11043        excerpt1_id = multibuffer
11044            .push_excerpts(
11045                buffer.clone(),
11046                [
11047                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11048                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11049                ],
11050                cx,
11051            )
11052            .into_iter()
11053            .next();
11054        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11055        multibuffer
11056    });
11057
11058    let editor = cx.add_window(|window, cx| {
11059        let mut editor = build_editor(multibuffer.clone(), window, cx);
11060        let snapshot = editor.snapshot(window, cx);
11061        editor.change_selections(None, window, cx, |s| {
11062            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11063        });
11064        editor.begin_selection(
11065            Point::new(2, 1).to_display_point(&snapshot),
11066            true,
11067            1,
11068            window,
11069            cx,
11070        );
11071        assert_eq!(
11072            editor.selections.ranges(cx),
11073            [
11074                Point::new(1, 3)..Point::new(1, 3),
11075                Point::new(2, 1)..Point::new(2, 1),
11076            ]
11077        );
11078        editor
11079    });
11080
11081    // Refreshing selections is a no-op when excerpts haven't changed.
11082    _ = editor.update(cx, |editor, window, cx| {
11083        editor.change_selections(None, window, cx, |s| s.refresh());
11084        assert_eq!(
11085            editor.selections.ranges(cx),
11086            [
11087                Point::new(1, 3)..Point::new(1, 3),
11088                Point::new(2, 1)..Point::new(2, 1),
11089            ]
11090        );
11091    });
11092
11093    multibuffer.update(cx, |multibuffer, cx| {
11094        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11095    });
11096    _ = editor.update(cx, |editor, window, cx| {
11097        // Removing an excerpt causes the first selection to become degenerate.
11098        assert_eq!(
11099            editor.selections.ranges(cx),
11100            [
11101                Point::new(0, 0)..Point::new(0, 0),
11102                Point::new(0, 1)..Point::new(0, 1)
11103            ]
11104        );
11105
11106        // Refreshing selections will relocate the first selection to the original buffer
11107        // location.
11108        editor.change_selections(None, window, cx, |s| s.refresh());
11109        assert_eq!(
11110            editor.selections.ranges(cx),
11111            [
11112                Point::new(0, 1)..Point::new(0, 1),
11113                Point::new(0, 3)..Point::new(0, 3)
11114            ]
11115        );
11116        assert!(editor.selections.pending_anchor().is_some());
11117    });
11118}
11119
11120#[gpui::test]
11121fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11122    init_test(cx, |_| {});
11123
11124    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11125    let mut excerpt1_id = None;
11126    let multibuffer = cx.new(|cx| {
11127        let mut multibuffer = MultiBuffer::new(ReadWrite);
11128        excerpt1_id = multibuffer
11129            .push_excerpts(
11130                buffer.clone(),
11131                [
11132                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11133                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11134                ],
11135                cx,
11136            )
11137            .into_iter()
11138            .next();
11139        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11140        multibuffer
11141    });
11142
11143    let editor = cx.add_window(|window, cx| {
11144        let mut editor = build_editor(multibuffer.clone(), window, cx);
11145        let snapshot = editor.snapshot(window, cx);
11146        editor.begin_selection(
11147            Point::new(1, 3).to_display_point(&snapshot),
11148            false,
11149            1,
11150            window,
11151            cx,
11152        );
11153        assert_eq!(
11154            editor.selections.ranges(cx),
11155            [Point::new(1, 3)..Point::new(1, 3)]
11156        );
11157        editor
11158    });
11159
11160    multibuffer.update(cx, |multibuffer, cx| {
11161        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11162    });
11163    _ = editor.update(cx, |editor, window, cx| {
11164        assert_eq!(
11165            editor.selections.ranges(cx),
11166            [Point::new(0, 0)..Point::new(0, 0)]
11167        );
11168
11169        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11170        editor.change_selections(None, window, cx, |s| s.refresh());
11171        assert_eq!(
11172            editor.selections.ranges(cx),
11173            [Point::new(0, 3)..Point::new(0, 3)]
11174        );
11175        assert!(editor.selections.pending_anchor().is_some());
11176    });
11177}
11178
11179#[gpui::test]
11180async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11181    init_test(cx, |_| {});
11182
11183    let language = Arc::new(
11184        Language::new(
11185            LanguageConfig {
11186                brackets: BracketPairConfig {
11187                    pairs: vec![
11188                        BracketPair {
11189                            start: "{".to_string(),
11190                            end: "}".to_string(),
11191                            close: true,
11192                            surround: true,
11193                            newline: true,
11194                        },
11195                        BracketPair {
11196                            start: "/* ".to_string(),
11197                            end: " */".to_string(),
11198                            close: true,
11199                            surround: true,
11200                            newline: true,
11201                        },
11202                    ],
11203                    ..Default::default()
11204                },
11205                ..Default::default()
11206            },
11207            Some(tree_sitter_rust::LANGUAGE.into()),
11208        )
11209        .with_indents_query("")
11210        .unwrap(),
11211    );
11212
11213    let text = concat!(
11214        "{   }\n",     //
11215        "  x\n",       //
11216        "  /*   */\n", //
11217        "x\n",         //
11218        "{{} }\n",     //
11219    );
11220
11221    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11222    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11223    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11224    editor
11225        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11226        .await;
11227
11228    editor.update_in(cx, |editor, window, cx| {
11229        editor.change_selections(None, window, cx, |s| {
11230            s.select_display_ranges([
11231                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11232                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11233                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11234            ])
11235        });
11236        editor.newline(&Newline, window, cx);
11237
11238        assert_eq!(
11239            editor.buffer().read(cx).read(cx).text(),
11240            concat!(
11241                "{ \n",    // Suppress rustfmt
11242                "\n",      //
11243                "}\n",     //
11244                "  x\n",   //
11245                "  /* \n", //
11246                "  \n",    //
11247                "  */\n",  //
11248                "x\n",     //
11249                "{{} \n",  //
11250                "}\n",     //
11251            )
11252        );
11253    });
11254}
11255
11256#[gpui::test]
11257fn test_highlighted_ranges(cx: &mut TestAppContext) {
11258    init_test(cx, |_| {});
11259
11260    let editor = cx.add_window(|window, cx| {
11261        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11262        build_editor(buffer.clone(), window, cx)
11263    });
11264
11265    _ = editor.update(cx, |editor, window, cx| {
11266        struct Type1;
11267        struct Type2;
11268
11269        let buffer = editor.buffer.read(cx).snapshot(cx);
11270
11271        let anchor_range =
11272            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11273
11274        editor.highlight_background::<Type1>(
11275            &[
11276                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11277                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11278                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11279                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11280            ],
11281            |_| Hsla::red(),
11282            cx,
11283        );
11284        editor.highlight_background::<Type2>(
11285            &[
11286                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11287                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11288                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11289                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11290            ],
11291            |_| Hsla::green(),
11292            cx,
11293        );
11294
11295        let snapshot = editor.snapshot(window, cx);
11296        let mut highlighted_ranges = editor.background_highlights_in_range(
11297            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11298            &snapshot,
11299            cx.theme().colors(),
11300        );
11301        // Enforce a consistent ordering based on color without relying on the ordering of the
11302        // highlight's `TypeId` which is non-executor.
11303        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11304        assert_eq!(
11305            highlighted_ranges,
11306            &[
11307                (
11308                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11309                    Hsla::red(),
11310                ),
11311                (
11312                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11313                    Hsla::red(),
11314                ),
11315                (
11316                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11317                    Hsla::green(),
11318                ),
11319                (
11320                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11321                    Hsla::green(),
11322                ),
11323            ]
11324        );
11325        assert_eq!(
11326            editor.background_highlights_in_range(
11327                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11328                &snapshot,
11329                cx.theme().colors(),
11330            ),
11331            &[(
11332                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11333                Hsla::red(),
11334            )]
11335        );
11336    });
11337}
11338
11339#[gpui::test]
11340async fn test_following(cx: &mut TestAppContext) {
11341    init_test(cx, |_| {});
11342
11343    let fs = FakeFs::new(cx.executor());
11344    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11345
11346    let buffer = project.update(cx, |project, cx| {
11347        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11348        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11349    });
11350    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11351    let follower = cx.update(|cx| {
11352        cx.open_window(
11353            WindowOptions {
11354                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11355                    gpui::Point::new(px(0.), px(0.)),
11356                    gpui::Point::new(px(10.), px(80.)),
11357                ))),
11358                ..Default::default()
11359            },
11360            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11361        )
11362        .unwrap()
11363    });
11364
11365    let is_still_following = Rc::new(RefCell::new(true));
11366    let follower_edit_event_count = Rc::new(RefCell::new(0));
11367    let pending_update = Rc::new(RefCell::new(None));
11368    let leader_entity = leader.root(cx).unwrap();
11369    let follower_entity = follower.root(cx).unwrap();
11370    _ = follower.update(cx, {
11371        let update = pending_update.clone();
11372        let is_still_following = is_still_following.clone();
11373        let follower_edit_event_count = follower_edit_event_count.clone();
11374        |_, window, cx| {
11375            cx.subscribe_in(
11376                &leader_entity,
11377                window,
11378                move |_, leader, event, window, cx| {
11379                    leader.read(cx).add_event_to_update_proto(
11380                        event,
11381                        &mut update.borrow_mut(),
11382                        window,
11383                        cx,
11384                    );
11385                },
11386            )
11387            .detach();
11388
11389            cx.subscribe_in(
11390                &follower_entity,
11391                window,
11392                move |_, _, event: &EditorEvent, _window, _cx| {
11393                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11394                        *is_still_following.borrow_mut() = false;
11395                    }
11396
11397                    if let EditorEvent::BufferEdited = event {
11398                        *follower_edit_event_count.borrow_mut() += 1;
11399                    }
11400                },
11401            )
11402            .detach();
11403        }
11404    });
11405
11406    // Update the selections only
11407    _ = leader.update(cx, |leader, window, cx| {
11408        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11409    });
11410    follower
11411        .update(cx, |follower, window, cx| {
11412            follower.apply_update_proto(
11413                &project,
11414                pending_update.borrow_mut().take().unwrap(),
11415                window,
11416                cx,
11417            )
11418        })
11419        .unwrap()
11420        .await
11421        .unwrap();
11422    _ = follower.update(cx, |follower, _, cx| {
11423        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11424    });
11425    assert!(*is_still_following.borrow());
11426    assert_eq!(*follower_edit_event_count.borrow(), 0);
11427
11428    // Update the scroll position only
11429    _ = leader.update(cx, |leader, window, cx| {
11430        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11431    });
11432    follower
11433        .update(cx, |follower, window, cx| {
11434            follower.apply_update_proto(
11435                &project,
11436                pending_update.borrow_mut().take().unwrap(),
11437                window,
11438                cx,
11439            )
11440        })
11441        .unwrap()
11442        .await
11443        .unwrap();
11444    assert_eq!(
11445        follower
11446            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11447            .unwrap(),
11448        gpui::Point::new(1.5, 3.5)
11449    );
11450    assert!(*is_still_following.borrow());
11451    assert_eq!(*follower_edit_event_count.borrow(), 0);
11452
11453    // Update the selections and scroll position. The follower's scroll position is updated
11454    // via autoscroll, not via the leader's exact scroll position.
11455    _ = leader.update(cx, |leader, window, cx| {
11456        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11457        leader.request_autoscroll(Autoscroll::newest(), cx);
11458        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11459    });
11460    follower
11461        .update(cx, |follower, window, cx| {
11462            follower.apply_update_proto(
11463                &project,
11464                pending_update.borrow_mut().take().unwrap(),
11465                window,
11466                cx,
11467            )
11468        })
11469        .unwrap()
11470        .await
11471        .unwrap();
11472    _ = follower.update(cx, |follower, _, cx| {
11473        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11474        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11475    });
11476    assert!(*is_still_following.borrow());
11477
11478    // Creating a pending selection that precedes another selection
11479    _ = leader.update(cx, |leader, window, cx| {
11480        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11481        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11482    });
11483    follower
11484        .update(cx, |follower, window, cx| {
11485            follower.apply_update_proto(
11486                &project,
11487                pending_update.borrow_mut().take().unwrap(),
11488                window,
11489                cx,
11490            )
11491        })
11492        .unwrap()
11493        .await
11494        .unwrap();
11495    _ = follower.update(cx, |follower, _, cx| {
11496        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11497    });
11498    assert!(*is_still_following.borrow());
11499
11500    // Extend the pending selection so that it surrounds another selection
11501    _ = leader.update(cx, |leader, window, cx| {
11502        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11503    });
11504    follower
11505        .update(cx, |follower, window, cx| {
11506            follower.apply_update_proto(
11507                &project,
11508                pending_update.borrow_mut().take().unwrap(),
11509                window,
11510                cx,
11511            )
11512        })
11513        .unwrap()
11514        .await
11515        .unwrap();
11516    _ = follower.update(cx, |follower, _, cx| {
11517        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11518    });
11519
11520    // Scrolling locally breaks the follow
11521    _ = follower.update(cx, |follower, window, cx| {
11522        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11523        follower.set_scroll_anchor(
11524            ScrollAnchor {
11525                anchor: top_anchor,
11526                offset: gpui::Point::new(0.0, 0.5),
11527            },
11528            window,
11529            cx,
11530        );
11531    });
11532    assert!(!(*is_still_following.borrow()));
11533}
11534
11535#[gpui::test]
11536async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11537    init_test(cx, |_| {});
11538
11539    let fs = FakeFs::new(cx.executor());
11540    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11541    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11542    let pane = workspace
11543        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11544        .unwrap();
11545
11546    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11547
11548    let leader = pane.update_in(cx, |_, window, cx| {
11549        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11550        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11551    });
11552
11553    // Start following the editor when it has no excerpts.
11554    let mut state_message =
11555        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11556    let workspace_entity = workspace.root(cx).unwrap();
11557    let follower_1 = cx
11558        .update_window(*workspace.deref(), |_, window, cx| {
11559            Editor::from_state_proto(
11560                workspace_entity,
11561                ViewId {
11562                    creator: Default::default(),
11563                    id: 0,
11564                },
11565                &mut state_message,
11566                window,
11567                cx,
11568            )
11569        })
11570        .unwrap()
11571        .unwrap()
11572        .await
11573        .unwrap();
11574
11575    let update_message = Rc::new(RefCell::new(None));
11576    follower_1.update_in(cx, {
11577        let update = update_message.clone();
11578        |_, window, cx| {
11579            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11580                leader.read(cx).add_event_to_update_proto(
11581                    event,
11582                    &mut update.borrow_mut(),
11583                    window,
11584                    cx,
11585                );
11586            })
11587            .detach();
11588        }
11589    });
11590
11591    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11592        (
11593            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11594            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11595        )
11596    });
11597
11598    // Insert some excerpts.
11599    leader.update(cx, |leader, cx| {
11600        leader.buffer.update(cx, |multibuffer, cx| {
11601            let excerpt_ids = multibuffer.push_excerpts(
11602                buffer_1.clone(),
11603                [
11604                    ExcerptRange::new(1..6),
11605                    ExcerptRange::new(12..15),
11606                    ExcerptRange::new(0..3),
11607                ],
11608                cx,
11609            );
11610            multibuffer.insert_excerpts_after(
11611                excerpt_ids[0],
11612                buffer_2.clone(),
11613                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11614                cx,
11615            );
11616        });
11617    });
11618
11619    // Apply the update of adding the excerpts.
11620    follower_1
11621        .update_in(cx, |follower, window, cx| {
11622            follower.apply_update_proto(
11623                &project,
11624                update_message.borrow().clone().unwrap(),
11625                window,
11626                cx,
11627            )
11628        })
11629        .await
11630        .unwrap();
11631    assert_eq!(
11632        follower_1.update(cx, |editor, cx| editor.text(cx)),
11633        leader.update(cx, |editor, cx| editor.text(cx))
11634    );
11635    update_message.borrow_mut().take();
11636
11637    // Start following separately after it already has excerpts.
11638    let mut state_message =
11639        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11640    let workspace_entity = workspace.root(cx).unwrap();
11641    let follower_2 = cx
11642        .update_window(*workspace.deref(), |_, window, cx| {
11643            Editor::from_state_proto(
11644                workspace_entity,
11645                ViewId {
11646                    creator: Default::default(),
11647                    id: 0,
11648                },
11649                &mut state_message,
11650                window,
11651                cx,
11652            )
11653        })
11654        .unwrap()
11655        .unwrap()
11656        .await
11657        .unwrap();
11658    assert_eq!(
11659        follower_2.update(cx, |editor, cx| editor.text(cx)),
11660        leader.update(cx, |editor, cx| editor.text(cx))
11661    );
11662
11663    // Remove some excerpts.
11664    leader.update(cx, |leader, cx| {
11665        leader.buffer.update(cx, |multibuffer, cx| {
11666            let excerpt_ids = multibuffer.excerpt_ids();
11667            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11668            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11669        });
11670    });
11671
11672    // Apply the update of removing the excerpts.
11673    follower_1
11674        .update_in(cx, |follower, window, cx| {
11675            follower.apply_update_proto(
11676                &project,
11677                update_message.borrow().clone().unwrap(),
11678                window,
11679                cx,
11680            )
11681        })
11682        .await
11683        .unwrap();
11684    follower_2
11685        .update_in(cx, |follower, window, cx| {
11686            follower.apply_update_proto(
11687                &project,
11688                update_message.borrow().clone().unwrap(),
11689                window,
11690                cx,
11691            )
11692        })
11693        .await
11694        .unwrap();
11695    update_message.borrow_mut().take();
11696    assert_eq!(
11697        follower_1.update(cx, |editor, cx| editor.text(cx)),
11698        leader.update(cx, |editor, cx| editor.text(cx))
11699    );
11700}
11701
11702#[gpui::test]
11703async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11704    init_test(cx, |_| {});
11705
11706    let mut cx = EditorTestContext::new(cx).await;
11707    let lsp_store =
11708        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11709
11710    cx.set_state(indoc! {"
11711        ˇfn func(abc def: i32) -> u32 {
11712        }
11713    "});
11714
11715    cx.update(|_, cx| {
11716        lsp_store.update(cx, |lsp_store, cx| {
11717            lsp_store
11718                .update_diagnostics(
11719                    LanguageServerId(0),
11720                    lsp::PublishDiagnosticsParams {
11721                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11722                        version: None,
11723                        diagnostics: vec![
11724                            lsp::Diagnostic {
11725                                range: lsp::Range::new(
11726                                    lsp::Position::new(0, 11),
11727                                    lsp::Position::new(0, 12),
11728                                ),
11729                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11730                                ..Default::default()
11731                            },
11732                            lsp::Diagnostic {
11733                                range: lsp::Range::new(
11734                                    lsp::Position::new(0, 12),
11735                                    lsp::Position::new(0, 15),
11736                                ),
11737                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11738                                ..Default::default()
11739                            },
11740                            lsp::Diagnostic {
11741                                range: lsp::Range::new(
11742                                    lsp::Position::new(0, 25),
11743                                    lsp::Position::new(0, 28),
11744                                ),
11745                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11746                                ..Default::default()
11747                            },
11748                        ],
11749                    },
11750                    &[],
11751                    cx,
11752                )
11753                .unwrap()
11754        });
11755    });
11756
11757    executor.run_until_parked();
11758
11759    cx.update_editor(|editor, window, cx| {
11760        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11761    });
11762
11763    cx.assert_editor_state(indoc! {"
11764        fn func(abc def: i32) -> ˇu32 {
11765        }
11766    "});
11767
11768    cx.update_editor(|editor, window, cx| {
11769        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11770    });
11771
11772    cx.assert_editor_state(indoc! {"
11773        fn func(abc ˇdef: i32) -> u32 {
11774        }
11775    "});
11776
11777    cx.update_editor(|editor, window, cx| {
11778        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11779    });
11780
11781    cx.assert_editor_state(indoc! {"
11782        fn func(abcˇ def: i32) -> u32 {
11783        }
11784    "});
11785
11786    cx.update_editor(|editor, window, cx| {
11787        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11788    });
11789
11790    cx.assert_editor_state(indoc! {"
11791        fn func(abc def: i32) -> ˇu32 {
11792        }
11793    "});
11794}
11795
11796#[gpui::test]
11797async fn cycle_through_same_place_diagnostics(
11798    executor: BackgroundExecutor,
11799    cx: &mut TestAppContext,
11800) {
11801    init_test(cx, |_| {});
11802
11803    let mut cx = EditorTestContext::new(cx).await;
11804    let lsp_store =
11805        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11806
11807    cx.set_state(indoc! {"
11808        ˇfn func(abc def: i32) -> u32 {
11809        }
11810    "});
11811
11812    cx.update(|_, cx| {
11813        lsp_store.update(cx, |lsp_store, cx| {
11814            lsp_store
11815                .update_diagnostics(
11816                    LanguageServerId(0),
11817                    lsp::PublishDiagnosticsParams {
11818                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11819                        version: None,
11820                        diagnostics: vec![
11821                            lsp::Diagnostic {
11822                                range: lsp::Range::new(
11823                                    lsp::Position::new(0, 11),
11824                                    lsp::Position::new(0, 12),
11825                                ),
11826                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11827                                ..Default::default()
11828                            },
11829                            lsp::Diagnostic {
11830                                range: lsp::Range::new(
11831                                    lsp::Position::new(0, 12),
11832                                    lsp::Position::new(0, 15),
11833                                ),
11834                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11835                                ..Default::default()
11836                            },
11837                            lsp::Diagnostic {
11838                                range: lsp::Range::new(
11839                                    lsp::Position::new(0, 12),
11840                                    lsp::Position::new(0, 15),
11841                                ),
11842                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11843                                ..Default::default()
11844                            },
11845                            lsp::Diagnostic {
11846                                range: lsp::Range::new(
11847                                    lsp::Position::new(0, 25),
11848                                    lsp::Position::new(0, 28),
11849                                ),
11850                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11851                                ..Default::default()
11852                            },
11853                        ],
11854                    },
11855                    &[],
11856                    cx,
11857                )
11858                .unwrap()
11859        });
11860    });
11861    executor.run_until_parked();
11862
11863    //// Backward
11864
11865    // Fourth diagnostic
11866    cx.update_editor(|editor, window, cx| {
11867        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11868    });
11869    cx.assert_editor_state(indoc! {"
11870        fn func(abc def: i32) -> ˇu32 {
11871        }
11872    "});
11873
11874    // Third diagnostic
11875    cx.update_editor(|editor, window, cx| {
11876        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11877    });
11878    cx.assert_editor_state(indoc! {"
11879        fn func(abc ˇdef: i32) -> u32 {
11880        }
11881    "});
11882
11883    // Second diagnostic, same place
11884    cx.update_editor(|editor, window, cx| {
11885        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11886    });
11887    cx.assert_editor_state(indoc! {"
11888        fn func(abc ˇdef: i32) -> u32 {
11889        }
11890    "});
11891
11892    // First diagnostic
11893    cx.update_editor(|editor, window, cx| {
11894        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11895    });
11896    cx.assert_editor_state(indoc! {"
11897        fn func(abcˇ def: i32) -> u32 {
11898        }
11899    "});
11900
11901    // Wrapped over, fourth diagnostic
11902    cx.update_editor(|editor, window, cx| {
11903        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11904    });
11905    cx.assert_editor_state(indoc! {"
11906        fn func(abc def: i32) -> ˇu32 {
11907        }
11908    "});
11909
11910    cx.update_editor(|editor, window, cx| {
11911        editor.move_to_beginning(&MoveToBeginning, window, cx);
11912    });
11913    cx.assert_editor_state(indoc! {"
11914        ˇfn func(abc def: i32) -> u32 {
11915        }
11916    "});
11917
11918    //// Forward
11919
11920    // First diagnostic
11921    cx.update_editor(|editor, window, cx| {
11922        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11923    });
11924    cx.assert_editor_state(indoc! {"
11925        fn func(abcˇ def: i32) -> u32 {
11926        }
11927    "});
11928
11929    // Second diagnostic
11930    cx.update_editor(|editor, window, cx| {
11931        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11932    });
11933    cx.assert_editor_state(indoc! {"
11934        fn func(abc ˇdef: i32) -> u32 {
11935        }
11936    "});
11937
11938    // Third diagnostic, same place
11939    cx.update_editor(|editor, window, cx| {
11940        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11941    });
11942    cx.assert_editor_state(indoc! {"
11943        fn func(abc ˇdef: i32) -> u32 {
11944        }
11945    "});
11946
11947    // Fourth diagnostic
11948    cx.update_editor(|editor, window, cx| {
11949        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11950    });
11951    cx.assert_editor_state(indoc! {"
11952        fn func(abc def: i32) -> ˇu32 {
11953        }
11954    "});
11955
11956    // Wrapped around, first diagnostic
11957    cx.update_editor(|editor, window, cx| {
11958        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11959    });
11960    cx.assert_editor_state(indoc! {"
11961        fn func(abcˇ def: i32) -> u32 {
11962        }
11963    "});
11964}
11965
11966#[gpui::test]
11967async fn active_diagnostics_dismiss_after_invalidation(
11968    executor: BackgroundExecutor,
11969    cx: &mut TestAppContext,
11970) {
11971    init_test(cx, |_| {});
11972
11973    let mut cx = EditorTestContext::new(cx).await;
11974    let lsp_store =
11975        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11976
11977    cx.set_state(indoc! {"
11978        ˇfn func(abc def: i32) -> u32 {
11979        }
11980    "});
11981
11982    let message = "Something's wrong!";
11983    cx.update(|_, cx| {
11984        lsp_store.update(cx, |lsp_store, cx| {
11985            lsp_store
11986                .update_diagnostics(
11987                    LanguageServerId(0),
11988                    lsp::PublishDiagnosticsParams {
11989                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11990                        version: None,
11991                        diagnostics: vec![lsp::Diagnostic {
11992                            range: lsp::Range::new(
11993                                lsp::Position::new(0, 11),
11994                                lsp::Position::new(0, 12),
11995                            ),
11996                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11997                            message: message.to_string(),
11998                            ..Default::default()
11999                        }],
12000                    },
12001                    &[],
12002                    cx,
12003                )
12004                .unwrap()
12005        });
12006    });
12007    executor.run_until_parked();
12008
12009    cx.update_editor(|editor, window, cx| {
12010        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12011        assert_eq!(
12012            editor
12013                .active_diagnostics
12014                .as_ref()
12015                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
12016            Some(message),
12017            "Should have a diagnostics group activated"
12018        );
12019    });
12020    cx.assert_editor_state(indoc! {"
12021        fn func(abcˇ def: i32) -> u32 {
12022        }
12023    "});
12024
12025    cx.update(|_, cx| {
12026        lsp_store.update(cx, |lsp_store, cx| {
12027            lsp_store
12028                .update_diagnostics(
12029                    LanguageServerId(0),
12030                    lsp::PublishDiagnosticsParams {
12031                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12032                        version: None,
12033                        diagnostics: Vec::new(),
12034                    },
12035                    &[],
12036                    cx,
12037                )
12038                .unwrap()
12039        });
12040    });
12041    executor.run_until_parked();
12042    cx.update_editor(|editor, _, _| {
12043        assert_eq!(
12044            editor.active_diagnostics, None,
12045            "After no diagnostics set to the editor, no diagnostics should be active"
12046        );
12047    });
12048    cx.assert_editor_state(indoc! {"
12049        fn func(abcˇ def: i32) -> u32 {
12050        }
12051    "});
12052
12053    cx.update_editor(|editor, window, cx| {
12054        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12055        assert_eq!(
12056            editor.active_diagnostics, None,
12057            "Should be no diagnostics to go to and activate"
12058        );
12059    });
12060    cx.assert_editor_state(indoc! {"
12061        fn func(abcˇ def: i32) -> u32 {
12062        }
12063    "});
12064}
12065
12066#[gpui::test]
12067async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12068    init_test(cx, |_| {});
12069
12070    let mut cx = EditorTestContext::new(cx).await;
12071
12072    cx.set_state(indoc! {"
12073        fn func(abˇc def: i32) -> u32 {
12074        }
12075    "});
12076    let lsp_store =
12077        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12078
12079    cx.update(|_, cx| {
12080        lsp_store.update(cx, |lsp_store, cx| {
12081            lsp_store.update_diagnostics(
12082                LanguageServerId(0),
12083                lsp::PublishDiagnosticsParams {
12084                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12085                    version: None,
12086                    diagnostics: vec![lsp::Diagnostic {
12087                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12088                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12089                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12090                        ..Default::default()
12091                    }],
12092                },
12093                &[],
12094                cx,
12095            )
12096        })
12097    }).unwrap();
12098    cx.run_until_parked();
12099    cx.update_editor(|editor, window, cx| {
12100        hover_popover::hover(editor, &Default::default(), window, cx)
12101    });
12102    cx.run_until_parked();
12103    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12104}
12105
12106#[gpui::test]
12107async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12108    init_test(cx, |_| {});
12109
12110    let mut cx = EditorTestContext::new(cx).await;
12111
12112    let diff_base = r#"
12113        use some::mod;
12114
12115        const A: u32 = 42;
12116
12117        fn main() {
12118            println!("hello");
12119
12120            println!("world");
12121        }
12122        "#
12123    .unindent();
12124
12125    // Edits are modified, removed, modified, added
12126    cx.set_state(
12127        &r#"
12128        use some::modified;
12129
12130        ˇ
12131        fn main() {
12132            println!("hello there");
12133
12134            println!("around the");
12135            println!("world");
12136        }
12137        "#
12138        .unindent(),
12139    );
12140
12141    cx.set_head_text(&diff_base);
12142    executor.run_until_parked();
12143
12144    cx.update_editor(|editor, window, cx| {
12145        //Wrap around the bottom of the buffer
12146        for _ in 0..3 {
12147            editor.go_to_next_hunk(&GoToHunk, window, cx);
12148        }
12149    });
12150
12151    cx.assert_editor_state(
12152        &r#"
12153        ˇuse some::modified;
12154
12155
12156        fn main() {
12157            println!("hello there");
12158
12159            println!("around the");
12160            println!("world");
12161        }
12162        "#
12163        .unindent(),
12164    );
12165
12166    cx.update_editor(|editor, window, cx| {
12167        //Wrap around the top of the buffer
12168        for _ in 0..2 {
12169            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12170        }
12171    });
12172
12173    cx.assert_editor_state(
12174        &r#"
12175        use some::modified;
12176
12177
12178        fn main() {
12179        ˇ    println!("hello there");
12180
12181            println!("around the");
12182            println!("world");
12183        }
12184        "#
12185        .unindent(),
12186    );
12187
12188    cx.update_editor(|editor, window, cx| {
12189        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12190    });
12191
12192    cx.assert_editor_state(
12193        &r#"
12194        use some::modified;
12195
12196        ˇ
12197        fn main() {
12198            println!("hello there");
12199
12200            println!("around the");
12201            println!("world");
12202        }
12203        "#
12204        .unindent(),
12205    );
12206
12207    cx.update_editor(|editor, window, cx| {
12208        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12209    });
12210
12211    cx.assert_editor_state(
12212        &r#"
12213        ˇuse some::modified;
12214
12215
12216        fn main() {
12217            println!("hello there");
12218
12219            println!("around the");
12220            println!("world");
12221        }
12222        "#
12223        .unindent(),
12224    );
12225
12226    cx.update_editor(|editor, window, cx| {
12227        for _ in 0..2 {
12228            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12229        }
12230    });
12231
12232    cx.assert_editor_state(
12233        &r#"
12234        use some::modified;
12235
12236
12237        fn main() {
12238        ˇ    println!("hello there");
12239
12240            println!("around the");
12241            println!("world");
12242        }
12243        "#
12244        .unindent(),
12245    );
12246
12247    cx.update_editor(|editor, window, cx| {
12248        editor.fold(&Fold, window, cx);
12249    });
12250
12251    cx.update_editor(|editor, window, cx| {
12252        editor.go_to_next_hunk(&GoToHunk, window, cx);
12253    });
12254
12255    cx.assert_editor_state(
12256        &r#"
12257        ˇuse some::modified;
12258
12259
12260        fn main() {
12261            println!("hello there");
12262
12263            println!("around the");
12264            println!("world");
12265        }
12266        "#
12267        .unindent(),
12268    );
12269}
12270
12271#[test]
12272fn test_split_words() {
12273    fn split(text: &str) -> Vec<&str> {
12274        split_words(text).collect()
12275    }
12276
12277    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12278    assert_eq!(split("hello_world"), &["hello_", "world"]);
12279    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12280    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12281    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12282    assert_eq!(split("helloworld"), &["helloworld"]);
12283
12284    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12285}
12286
12287#[gpui::test]
12288async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12289    init_test(cx, |_| {});
12290
12291    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12292    let mut assert = |before, after| {
12293        let _state_context = cx.set_state(before);
12294        cx.run_until_parked();
12295        cx.update_editor(|editor, window, cx| {
12296            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12297        });
12298        cx.run_until_parked();
12299        cx.assert_editor_state(after);
12300    };
12301
12302    // Outside bracket jumps to outside of matching bracket
12303    assert("console.logˇ(var);", "console.log(var)ˇ;");
12304    assert("console.log(var)ˇ;", "console.logˇ(var);");
12305
12306    // Inside bracket jumps to inside of matching bracket
12307    assert("console.log(ˇvar);", "console.log(varˇ);");
12308    assert("console.log(varˇ);", "console.log(ˇvar);");
12309
12310    // When outside a bracket and inside, favor jumping to the inside bracket
12311    assert(
12312        "console.log('foo', [1, 2, 3]ˇ);",
12313        "console.log(ˇ'foo', [1, 2, 3]);",
12314    );
12315    assert(
12316        "console.log(ˇ'foo', [1, 2, 3]);",
12317        "console.log('foo', [1, 2, 3]ˇ);",
12318    );
12319
12320    // Bias forward if two options are equally likely
12321    assert(
12322        "let result = curried_fun()ˇ();",
12323        "let result = curried_fun()()ˇ;",
12324    );
12325
12326    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12327    assert(
12328        indoc! {"
12329            function test() {
12330                console.log('test')ˇ
12331            }"},
12332        indoc! {"
12333            function test() {
12334                console.logˇ('test')
12335            }"},
12336    );
12337}
12338
12339#[gpui::test]
12340async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12341    init_test(cx, |_| {});
12342
12343    let fs = FakeFs::new(cx.executor());
12344    fs.insert_tree(
12345        path!("/a"),
12346        json!({
12347            "main.rs": "fn main() { let a = 5; }",
12348            "other.rs": "// Test file",
12349        }),
12350    )
12351    .await;
12352    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12353
12354    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12355    language_registry.add(Arc::new(Language::new(
12356        LanguageConfig {
12357            name: "Rust".into(),
12358            matcher: LanguageMatcher {
12359                path_suffixes: vec!["rs".to_string()],
12360                ..Default::default()
12361            },
12362            brackets: BracketPairConfig {
12363                pairs: vec![BracketPair {
12364                    start: "{".to_string(),
12365                    end: "}".to_string(),
12366                    close: true,
12367                    surround: true,
12368                    newline: true,
12369                }],
12370                disabled_scopes_by_bracket_ix: Vec::new(),
12371            },
12372            ..Default::default()
12373        },
12374        Some(tree_sitter_rust::LANGUAGE.into()),
12375    )));
12376    let mut fake_servers = language_registry.register_fake_lsp(
12377        "Rust",
12378        FakeLspAdapter {
12379            capabilities: lsp::ServerCapabilities {
12380                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12381                    first_trigger_character: "{".to_string(),
12382                    more_trigger_character: None,
12383                }),
12384                ..Default::default()
12385            },
12386            ..Default::default()
12387        },
12388    );
12389
12390    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12391
12392    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12393
12394    let worktree_id = workspace
12395        .update(cx, |workspace, _, cx| {
12396            workspace.project().update(cx, |project, cx| {
12397                project.worktrees(cx).next().unwrap().read(cx).id()
12398            })
12399        })
12400        .unwrap();
12401
12402    let buffer = project
12403        .update(cx, |project, cx| {
12404            project.open_local_buffer(path!("/a/main.rs"), cx)
12405        })
12406        .await
12407        .unwrap();
12408    let editor_handle = workspace
12409        .update(cx, |workspace, window, cx| {
12410            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12411        })
12412        .unwrap()
12413        .await
12414        .unwrap()
12415        .downcast::<Editor>()
12416        .unwrap();
12417
12418    cx.executor().start_waiting();
12419    let fake_server = fake_servers.next().await.unwrap();
12420
12421    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12422        |params, _| async move {
12423            assert_eq!(
12424                params.text_document_position.text_document.uri,
12425                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12426            );
12427            assert_eq!(
12428                params.text_document_position.position,
12429                lsp::Position::new(0, 21),
12430            );
12431
12432            Ok(Some(vec![lsp::TextEdit {
12433                new_text: "]".to_string(),
12434                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12435            }]))
12436        },
12437    );
12438
12439    editor_handle.update_in(cx, |editor, window, cx| {
12440        window.focus(&editor.focus_handle(cx));
12441        editor.change_selections(None, window, cx, |s| {
12442            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12443        });
12444        editor.handle_input("{", window, cx);
12445    });
12446
12447    cx.executor().run_until_parked();
12448
12449    buffer.update(cx, |buffer, _| {
12450        assert_eq!(
12451            buffer.text(),
12452            "fn main() { let a = {5}; }",
12453            "No extra braces from on type formatting should appear in the buffer"
12454        )
12455    });
12456}
12457
12458#[gpui::test]
12459async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12460    init_test(cx, |_| {});
12461
12462    let fs = FakeFs::new(cx.executor());
12463    fs.insert_tree(
12464        path!("/a"),
12465        json!({
12466            "main.rs": "fn main() { let a = 5; }",
12467            "other.rs": "// Test file",
12468        }),
12469    )
12470    .await;
12471
12472    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12473
12474    let server_restarts = Arc::new(AtomicUsize::new(0));
12475    let closure_restarts = Arc::clone(&server_restarts);
12476    let language_server_name = "test language server";
12477    let language_name: LanguageName = "Rust".into();
12478
12479    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12480    language_registry.add(Arc::new(Language::new(
12481        LanguageConfig {
12482            name: language_name.clone(),
12483            matcher: LanguageMatcher {
12484                path_suffixes: vec!["rs".to_string()],
12485                ..Default::default()
12486            },
12487            ..Default::default()
12488        },
12489        Some(tree_sitter_rust::LANGUAGE.into()),
12490    )));
12491    let mut fake_servers = language_registry.register_fake_lsp(
12492        "Rust",
12493        FakeLspAdapter {
12494            name: language_server_name,
12495            initialization_options: Some(json!({
12496                "testOptionValue": true
12497            })),
12498            initializer: Some(Box::new(move |fake_server| {
12499                let task_restarts = Arc::clone(&closure_restarts);
12500                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12501                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12502                    futures::future::ready(Ok(()))
12503                });
12504            })),
12505            ..Default::default()
12506        },
12507    );
12508
12509    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12510    let _buffer = project
12511        .update(cx, |project, cx| {
12512            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12513        })
12514        .await
12515        .unwrap();
12516    let _fake_server = fake_servers.next().await.unwrap();
12517    update_test_language_settings(cx, |language_settings| {
12518        language_settings.languages.insert(
12519            language_name.clone(),
12520            LanguageSettingsContent {
12521                tab_size: NonZeroU32::new(8),
12522                ..Default::default()
12523            },
12524        );
12525    });
12526    cx.executor().run_until_parked();
12527    assert_eq!(
12528        server_restarts.load(atomic::Ordering::Acquire),
12529        0,
12530        "Should not restart LSP server on an unrelated change"
12531    );
12532
12533    update_test_project_settings(cx, |project_settings| {
12534        project_settings.lsp.insert(
12535            "Some other server name".into(),
12536            LspSettings {
12537                binary: None,
12538                settings: None,
12539                initialization_options: Some(json!({
12540                    "some other init value": false
12541                })),
12542                enable_lsp_tasks: false,
12543            },
12544        );
12545    });
12546    cx.executor().run_until_parked();
12547    assert_eq!(
12548        server_restarts.load(atomic::Ordering::Acquire),
12549        0,
12550        "Should not restart LSP server on an unrelated LSP settings change"
12551    );
12552
12553    update_test_project_settings(cx, |project_settings| {
12554        project_settings.lsp.insert(
12555            language_server_name.into(),
12556            LspSettings {
12557                binary: None,
12558                settings: None,
12559                initialization_options: Some(json!({
12560                    "anotherInitValue": false
12561                })),
12562                enable_lsp_tasks: false,
12563            },
12564        );
12565    });
12566    cx.executor().run_until_parked();
12567    assert_eq!(
12568        server_restarts.load(atomic::Ordering::Acquire),
12569        1,
12570        "Should restart LSP server on a related LSP settings change"
12571    );
12572
12573    update_test_project_settings(cx, |project_settings| {
12574        project_settings.lsp.insert(
12575            language_server_name.into(),
12576            LspSettings {
12577                binary: None,
12578                settings: None,
12579                initialization_options: Some(json!({
12580                    "anotherInitValue": false
12581                })),
12582                enable_lsp_tasks: false,
12583            },
12584        );
12585    });
12586    cx.executor().run_until_parked();
12587    assert_eq!(
12588        server_restarts.load(atomic::Ordering::Acquire),
12589        1,
12590        "Should not restart LSP server on a related LSP settings change that is the same"
12591    );
12592
12593    update_test_project_settings(cx, |project_settings| {
12594        project_settings.lsp.insert(
12595            language_server_name.into(),
12596            LspSettings {
12597                binary: None,
12598                settings: None,
12599                initialization_options: None,
12600                enable_lsp_tasks: false,
12601            },
12602        );
12603    });
12604    cx.executor().run_until_parked();
12605    assert_eq!(
12606        server_restarts.load(atomic::Ordering::Acquire),
12607        2,
12608        "Should restart LSP server on another related LSP settings change"
12609    );
12610}
12611
12612#[gpui::test]
12613async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12614    init_test(cx, |_| {});
12615
12616    let mut cx = EditorLspTestContext::new_rust(
12617        lsp::ServerCapabilities {
12618            completion_provider: Some(lsp::CompletionOptions {
12619                trigger_characters: Some(vec![".".to_string()]),
12620                resolve_provider: Some(true),
12621                ..Default::default()
12622            }),
12623            ..Default::default()
12624        },
12625        cx,
12626    )
12627    .await;
12628
12629    cx.set_state("fn main() { let a = 2ˇ; }");
12630    cx.simulate_keystroke(".");
12631    let completion_item = lsp::CompletionItem {
12632        label: "some".into(),
12633        kind: Some(lsp::CompletionItemKind::SNIPPET),
12634        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12635        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12636            kind: lsp::MarkupKind::Markdown,
12637            value: "```rust\nSome(2)\n```".to_string(),
12638        })),
12639        deprecated: Some(false),
12640        sort_text: Some("fffffff2".to_string()),
12641        filter_text: Some("some".to_string()),
12642        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12643        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12644            range: lsp::Range {
12645                start: lsp::Position {
12646                    line: 0,
12647                    character: 22,
12648                },
12649                end: lsp::Position {
12650                    line: 0,
12651                    character: 22,
12652                },
12653            },
12654            new_text: "Some(2)".to_string(),
12655        })),
12656        additional_text_edits: Some(vec![lsp::TextEdit {
12657            range: lsp::Range {
12658                start: lsp::Position {
12659                    line: 0,
12660                    character: 20,
12661                },
12662                end: lsp::Position {
12663                    line: 0,
12664                    character: 22,
12665                },
12666            },
12667            new_text: "".to_string(),
12668        }]),
12669        ..Default::default()
12670    };
12671
12672    let closure_completion_item = completion_item.clone();
12673    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12674        let task_completion_item = closure_completion_item.clone();
12675        async move {
12676            Ok(Some(lsp::CompletionResponse::Array(vec![
12677                task_completion_item,
12678            ])))
12679        }
12680    });
12681
12682    request.next().await;
12683
12684    cx.condition(|editor, _| editor.context_menu_visible())
12685        .await;
12686    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12687        editor
12688            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12689            .unwrap()
12690    });
12691    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12692
12693    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12694        let task_completion_item = completion_item.clone();
12695        async move { Ok(task_completion_item) }
12696    })
12697    .next()
12698    .await
12699    .unwrap();
12700    apply_additional_edits.await.unwrap();
12701    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12702}
12703
12704#[gpui::test]
12705async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12706    init_test(cx, |_| {});
12707
12708    let mut cx = EditorLspTestContext::new_rust(
12709        lsp::ServerCapabilities {
12710            completion_provider: Some(lsp::CompletionOptions {
12711                trigger_characters: Some(vec![".".to_string()]),
12712                resolve_provider: Some(true),
12713                ..Default::default()
12714            }),
12715            ..Default::default()
12716        },
12717        cx,
12718    )
12719    .await;
12720
12721    cx.set_state("fn main() { let a = 2ˇ; }");
12722    cx.simulate_keystroke(".");
12723
12724    let item1 = lsp::CompletionItem {
12725        label: "method id()".to_string(),
12726        filter_text: Some("id".to_string()),
12727        detail: None,
12728        documentation: None,
12729        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12730            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12731            new_text: ".id".to_string(),
12732        })),
12733        ..lsp::CompletionItem::default()
12734    };
12735
12736    let item2 = lsp::CompletionItem {
12737        label: "other".to_string(),
12738        filter_text: Some("other".to_string()),
12739        detail: None,
12740        documentation: None,
12741        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12742            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12743            new_text: ".other".to_string(),
12744        })),
12745        ..lsp::CompletionItem::default()
12746    };
12747
12748    let item1 = item1.clone();
12749    cx.set_request_handler::<lsp::request::Completion, _, _>({
12750        let item1 = item1.clone();
12751        move |_, _, _| {
12752            let item1 = item1.clone();
12753            let item2 = item2.clone();
12754            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12755        }
12756    })
12757    .next()
12758    .await;
12759
12760    cx.condition(|editor, _| editor.context_menu_visible())
12761        .await;
12762    cx.update_editor(|editor, _, _| {
12763        let context_menu = editor.context_menu.borrow_mut();
12764        let context_menu = context_menu
12765            .as_ref()
12766            .expect("Should have the context menu deployed");
12767        match context_menu {
12768            CodeContextMenu::Completions(completions_menu) => {
12769                let completions = completions_menu.completions.borrow_mut();
12770                assert_eq!(
12771                    completions
12772                        .iter()
12773                        .map(|completion| &completion.label.text)
12774                        .collect::<Vec<_>>(),
12775                    vec!["method id()", "other"]
12776                )
12777            }
12778            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12779        }
12780    });
12781
12782    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12783        let item1 = item1.clone();
12784        move |_, item_to_resolve, _| {
12785            let item1 = item1.clone();
12786            async move {
12787                if item1 == item_to_resolve {
12788                    Ok(lsp::CompletionItem {
12789                        label: "method id()".to_string(),
12790                        filter_text: Some("id".to_string()),
12791                        detail: Some("Now resolved!".to_string()),
12792                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12793                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12794                            range: lsp::Range::new(
12795                                lsp::Position::new(0, 22),
12796                                lsp::Position::new(0, 22),
12797                            ),
12798                            new_text: ".id".to_string(),
12799                        })),
12800                        ..lsp::CompletionItem::default()
12801                    })
12802                } else {
12803                    Ok(item_to_resolve)
12804                }
12805            }
12806        }
12807    })
12808    .next()
12809    .await
12810    .unwrap();
12811    cx.run_until_parked();
12812
12813    cx.update_editor(|editor, window, cx| {
12814        editor.context_menu_next(&Default::default(), window, cx);
12815    });
12816
12817    cx.update_editor(|editor, _, _| {
12818        let context_menu = editor.context_menu.borrow_mut();
12819        let context_menu = context_menu
12820            .as_ref()
12821            .expect("Should have the context menu deployed");
12822        match context_menu {
12823            CodeContextMenu::Completions(completions_menu) => {
12824                let completions = completions_menu.completions.borrow_mut();
12825                assert_eq!(
12826                    completions
12827                        .iter()
12828                        .map(|completion| &completion.label.text)
12829                        .collect::<Vec<_>>(),
12830                    vec!["method id() Now resolved!", "other"],
12831                    "Should update first completion label, but not second as the filter text did not match."
12832                );
12833            }
12834            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12835        }
12836    });
12837}
12838
12839#[gpui::test]
12840async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12841    init_test(cx, |_| {});
12842
12843    let mut cx = EditorLspTestContext::new_rust(
12844        lsp::ServerCapabilities {
12845            completion_provider: Some(lsp::CompletionOptions {
12846                trigger_characters: Some(vec![".".to_string()]),
12847                resolve_provider: Some(true),
12848                ..Default::default()
12849            }),
12850            ..Default::default()
12851        },
12852        cx,
12853    )
12854    .await;
12855
12856    cx.set_state("fn main() { let a = 2ˇ; }");
12857    cx.simulate_keystroke(".");
12858
12859    let unresolved_item_1 = lsp::CompletionItem {
12860        label: "id".to_string(),
12861        filter_text: Some("id".to_string()),
12862        detail: None,
12863        documentation: None,
12864        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12865            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12866            new_text: ".id".to_string(),
12867        })),
12868        ..lsp::CompletionItem::default()
12869    };
12870    let resolved_item_1 = lsp::CompletionItem {
12871        additional_text_edits: Some(vec![lsp::TextEdit {
12872            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12873            new_text: "!!".to_string(),
12874        }]),
12875        ..unresolved_item_1.clone()
12876    };
12877    let unresolved_item_2 = lsp::CompletionItem {
12878        label: "other".to_string(),
12879        filter_text: Some("other".to_string()),
12880        detail: None,
12881        documentation: None,
12882        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12883            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12884            new_text: ".other".to_string(),
12885        })),
12886        ..lsp::CompletionItem::default()
12887    };
12888    let resolved_item_2 = lsp::CompletionItem {
12889        additional_text_edits: Some(vec![lsp::TextEdit {
12890            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12891            new_text: "??".to_string(),
12892        }]),
12893        ..unresolved_item_2.clone()
12894    };
12895
12896    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12897    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12898    cx.lsp
12899        .server
12900        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12901            let unresolved_item_1 = unresolved_item_1.clone();
12902            let resolved_item_1 = resolved_item_1.clone();
12903            let unresolved_item_2 = unresolved_item_2.clone();
12904            let resolved_item_2 = resolved_item_2.clone();
12905            let resolve_requests_1 = resolve_requests_1.clone();
12906            let resolve_requests_2 = resolve_requests_2.clone();
12907            move |unresolved_request, _| {
12908                let unresolved_item_1 = unresolved_item_1.clone();
12909                let resolved_item_1 = resolved_item_1.clone();
12910                let unresolved_item_2 = unresolved_item_2.clone();
12911                let resolved_item_2 = resolved_item_2.clone();
12912                let resolve_requests_1 = resolve_requests_1.clone();
12913                let resolve_requests_2 = resolve_requests_2.clone();
12914                async move {
12915                    if unresolved_request == unresolved_item_1 {
12916                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12917                        Ok(resolved_item_1.clone())
12918                    } else if unresolved_request == unresolved_item_2 {
12919                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12920                        Ok(resolved_item_2.clone())
12921                    } else {
12922                        panic!("Unexpected completion item {unresolved_request:?}")
12923                    }
12924                }
12925            }
12926        })
12927        .detach();
12928
12929    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12930        let unresolved_item_1 = unresolved_item_1.clone();
12931        let unresolved_item_2 = unresolved_item_2.clone();
12932        async move {
12933            Ok(Some(lsp::CompletionResponse::Array(vec![
12934                unresolved_item_1,
12935                unresolved_item_2,
12936            ])))
12937        }
12938    })
12939    .next()
12940    .await;
12941
12942    cx.condition(|editor, _| editor.context_menu_visible())
12943        .await;
12944    cx.update_editor(|editor, _, _| {
12945        let context_menu = editor.context_menu.borrow_mut();
12946        let context_menu = context_menu
12947            .as_ref()
12948            .expect("Should have the context menu deployed");
12949        match context_menu {
12950            CodeContextMenu::Completions(completions_menu) => {
12951                let completions = completions_menu.completions.borrow_mut();
12952                assert_eq!(
12953                    completions
12954                        .iter()
12955                        .map(|completion| &completion.label.text)
12956                        .collect::<Vec<_>>(),
12957                    vec!["id", "other"]
12958                )
12959            }
12960            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12961        }
12962    });
12963    cx.run_until_parked();
12964
12965    cx.update_editor(|editor, window, cx| {
12966        editor.context_menu_next(&ContextMenuNext, window, cx);
12967    });
12968    cx.run_until_parked();
12969    cx.update_editor(|editor, window, cx| {
12970        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12971    });
12972    cx.run_until_parked();
12973    cx.update_editor(|editor, window, cx| {
12974        editor.context_menu_next(&ContextMenuNext, window, cx);
12975    });
12976    cx.run_until_parked();
12977    cx.update_editor(|editor, window, cx| {
12978        editor
12979            .compose_completion(&ComposeCompletion::default(), window, cx)
12980            .expect("No task returned")
12981    })
12982    .await
12983    .expect("Completion failed");
12984    cx.run_until_parked();
12985
12986    cx.update_editor(|editor, _, cx| {
12987        assert_eq!(
12988            resolve_requests_1.load(atomic::Ordering::Acquire),
12989            1,
12990            "Should always resolve once despite multiple selections"
12991        );
12992        assert_eq!(
12993            resolve_requests_2.load(atomic::Ordering::Acquire),
12994            1,
12995            "Should always resolve once after multiple selections and applying the completion"
12996        );
12997        assert_eq!(
12998            editor.text(cx),
12999            "fn main() { let a = ??.other; }",
13000            "Should use resolved data when applying the completion"
13001        );
13002    });
13003}
13004
13005#[gpui::test]
13006async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13007    init_test(cx, |_| {});
13008
13009    let item_0 = lsp::CompletionItem {
13010        label: "abs".into(),
13011        insert_text: Some("abs".into()),
13012        data: Some(json!({ "very": "special"})),
13013        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13014        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13015            lsp::InsertReplaceEdit {
13016                new_text: "abs".to_string(),
13017                insert: lsp::Range::default(),
13018                replace: lsp::Range::default(),
13019            },
13020        )),
13021        ..lsp::CompletionItem::default()
13022    };
13023    let items = iter::once(item_0.clone())
13024        .chain((11..51).map(|i| lsp::CompletionItem {
13025            label: format!("item_{}", i),
13026            insert_text: Some(format!("item_{}", i)),
13027            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13028            ..lsp::CompletionItem::default()
13029        }))
13030        .collect::<Vec<_>>();
13031
13032    let default_commit_characters = vec!["?".to_string()];
13033    let default_data = json!({ "default": "data"});
13034    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13035    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13036    let default_edit_range = lsp::Range {
13037        start: lsp::Position {
13038            line: 0,
13039            character: 5,
13040        },
13041        end: lsp::Position {
13042            line: 0,
13043            character: 5,
13044        },
13045    };
13046
13047    let mut cx = EditorLspTestContext::new_rust(
13048        lsp::ServerCapabilities {
13049            completion_provider: Some(lsp::CompletionOptions {
13050                trigger_characters: Some(vec![".".to_string()]),
13051                resolve_provider: Some(true),
13052                ..Default::default()
13053            }),
13054            ..Default::default()
13055        },
13056        cx,
13057    )
13058    .await;
13059
13060    cx.set_state("fn main() { let a = 2ˇ; }");
13061    cx.simulate_keystroke(".");
13062
13063    let completion_data = default_data.clone();
13064    let completion_characters = default_commit_characters.clone();
13065    let completion_items = items.clone();
13066    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13067        let default_data = completion_data.clone();
13068        let default_commit_characters = completion_characters.clone();
13069        let items = completion_items.clone();
13070        async move {
13071            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13072                items,
13073                item_defaults: Some(lsp::CompletionListItemDefaults {
13074                    data: Some(default_data.clone()),
13075                    commit_characters: Some(default_commit_characters.clone()),
13076                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13077                        default_edit_range,
13078                    )),
13079                    insert_text_format: Some(default_insert_text_format),
13080                    insert_text_mode: Some(default_insert_text_mode),
13081                }),
13082                ..lsp::CompletionList::default()
13083            })))
13084        }
13085    })
13086    .next()
13087    .await;
13088
13089    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13090    cx.lsp
13091        .server
13092        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13093            let closure_resolved_items = resolved_items.clone();
13094            move |item_to_resolve, _| {
13095                let closure_resolved_items = closure_resolved_items.clone();
13096                async move {
13097                    closure_resolved_items.lock().push(item_to_resolve.clone());
13098                    Ok(item_to_resolve)
13099                }
13100            }
13101        })
13102        .detach();
13103
13104    cx.condition(|editor, _| editor.context_menu_visible())
13105        .await;
13106    cx.run_until_parked();
13107    cx.update_editor(|editor, _, _| {
13108        let menu = editor.context_menu.borrow_mut();
13109        match menu.as_ref().expect("should have the completions menu") {
13110            CodeContextMenu::Completions(completions_menu) => {
13111                assert_eq!(
13112                    completions_menu
13113                        .entries
13114                        .borrow()
13115                        .iter()
13116                        .map(|mat| mat.string.clone())
13117                        .collect::<Vec<String>>(),
13118                    items
13119                        .iter()
13120                        .map(|completion| completion.label.clone())
13121                        .collect::<Vec<String>>()
13122                );
13123            }
13124            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13125        }
13126    });
13127    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13128    // with 4 from the end.
13129    assert_eq!(
13130        *resolved_items.lock(),
13131        [&items[0..16], &items[items.len() - 4..items.len()]]
13132            .concat()
13133            .iter()
13134            .cloned()
13135            .map(|mut item| {
13136                if item.data.is_none() {
13137                    item.data = Some(default_data.clone());
13138                }
13139                item
13140            })
13141            .collect::<Vec<lsp::CompletionItem>>(),
13142        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13143    );
13144    resolved_items.lock().clear();
13145
13146    cx.update_editor(|editor, window, cx| {
13147        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13148    });
13149    cx.run_until_parked();
13150    // Completions that have already been resolved are skipped.
13151    assert_eq!(
13152        *resolved_items.lock(),
13153        items[items.len() - 16..items.len() - 4]
13154            .iter()
13155            .cloned()
13156            .map(|mut item| {
13157                if item.data.is_none() {
13158                    item.data = Some(default_data.clone());
13159                }
13160                item
13161            })
13162            .collect::<Vec<lsp::CompletionItem>>()
13163    );
13164    resolved_items.lock().clear();
13165}
13166
13167#[gpui::test]
13168async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13169    init_test(cx, |_| {});
13170
13171    let mut cx = EditorLspTestContext::new(
13172        Language::new(
13173            LanguageConfig {
13174                matcher: LanguageMatcher {
13175                    path_suffixes: vec!["jsx".into()],
13176                    ..Default::default()
13177                },
13178                overrides: [(
13179                    "element".into(),
13180                    LanguageConfigOverride {
13181                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13182                        ..Default::default()
13183                    },
13184                )]
13185                .into_iter()
13186                .collect(),
13187                ..Default::default()
13188            },
13189            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13190        )
13191        .with_override_query("(jsx_self_closing_element) @element")
13192        .unwrap(),
13193        lsp::ServerCapabilities {
13194            completion_provider: Some(lsp::CompletionOptions {
13195                trigger_characters: Some(vec![":".to_string()]),
13196                ..Default::default()
13197            }),
13198            ..Default::default()
13199        },
13200        cx,
13201    )
13202    .await;
13203
13204    cx.lsp
13205        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13206            Ok(Some(lsp::CompletionResponse::Array(vec![
13207                lsp::CompletionItem {
13208                    label: "bg-blue".into(),
13209                    ..Default::default()
13210                },
13211                lsp::CompletionItem {
13212                    label: "bg-red".into(),
13213                    ..Default::default()
13214                },
13215                lsp::CompletionItem {
13216                    label: "bg-yellow".into(),
13217                    ..Default::default()
13218                },
13219            ])))
13220        });
13221
13222    cx.set_state(r#"<p class="bgˇ" />"#);
13223
13224    // Trigger completion when typing a dash, because the dash is an extra
13225    // word character in the 'element' scope, which contains the cursor.
13226    cx.simulate_keystroke("-");
13227    cx.executor().run_until_parked();
13228    cx.update_editor(|editor, _, _| {
13229        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13230        {
13231            assert_eq!(
13232                completion_menu_entries(&menu),
13233                &["bg-red", "bg-blue", "bg-yellow"]
13234            );
13235        } else {
13236            panic!("expected completion menu to be open");
13237        }
13238    });
13239
13240    cx.simulate_keystroke("l");
13241    cx.executor().run_until_parked();
13242    cx.update_editor(|editor, _, _| {
13243        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13244        {
13245            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13246        } else {
13247            panic!("expected completion menu to be open");
13248        }
13249    });
13250
13251    // When filtering completions, consider the character after the '-' to
13252    // be the start of a subword.
13253    cx.set_state(r#"<p class="yelˇ" />"#);
13254    cx.simulate_keystroke("l");
13255    cx.executor().run_until_parked();
13256    cx.update_editor(|editor, _, _| {
13257        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13258        {
13259            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13260        } else {
13261            panic!("expected completion menu to be open");
13262        }
13263    });
13264}
13265
13266fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13267    let entries = menu.entries.borrow();
13268    entries.iter().map(|mat| mat.string.clone()).collect()
13269}
13270
13271#[gpui::test]
13272async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13273    init_test(cx, |settings| {
13274        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13275            FormatterList(vec![Formatter::Prettier].into()),
13276        ))
13277    });
13278
13279    let fs = FakeFs::new(cx.executor());
13280    fs.insert_file(path!("/file.ts"), Default::default()).await;
13281
13282    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13283    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13284
13285    language_registry.add(Arc::new(Language::new(
13286        LanguageConfig {
13287            name: "TypeScript".into(),
13288            matcher: LanguageMatcher {
13289                path_suffixes: vec!["ts".to_string()],
13290                ..Default::default()
13291            },
13292            ..Default::default()
13293        },
13294        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13295    )));
13296    update_test_language_settings(cx, |settings| {
13297        settings.defaults.prettier = Some(PrettierSettings {
13298            allowed: true,
13299            ..PrettierSettings::default()
13300        });
13301    });
13302
13303    let test_plugin = "test_plugin";
13304    let _ = language_registry.register_fake_lsp(
13305        "TypeScript",
13306        FakeLspAdapter {
13307            prettier_plugins: vec![test_plugin],
13308            ..Default::default()
13309        },
13310    );
13311
13312    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13313    let buffer = project
13314        .update(cx, |project, cx| {
13315            project.open_local_buffer(path!("/file.ts"), cx)
13316        })
13317        .await
13318        .unwrap();
13319
13320    let buffer_text = "one\ntwo\nthree\n";
13321    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13322    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13323    editor.update_in(cx, |editor, window, cx| {
13324        editor.set_text(buffer_text, window, cx)
13325    });
13326
13327    editor
13328        .update_in(cx, |editor, window, cx| {
13329            editor.perform_format(
13330                project.clone(),
13331                FormatTrigger::Manual,
13332                FormatTarget::Buffers,
13333                window,
13334                cx,
13335            )
13336        })
13337        .unwrap()
13338        .await;
13339    assert_eq!(
13340        editor.update(cx, |editor, cx| editor.text(cx)),
13341        buffer_text.to_string() + prettier_format_suffix,
13342        "Test prettier formatting was not applied to the original buffer text",
13343    );
13344
13345    update_test_language_settings(cx, |settings| {
13346        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13347    });
13348    let format = editor.update_in(cx, |editor, window, cx| {
13349        editor.perform_format(
13350            project.clone(),
13351            FormatTrigger::Manual,
13352            FormatTarget::Buffers,
13353            window,
13354            cx,
13355        )
13356    });
13357    format.await.unwrap();
13358    assert_eq!(
13359        editor.update(cx, |editor, cx| editor.text(cx)),
13360        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13361        "Autoformatting (via test prettier) was not applied to the original buffer text",
13362    );
13363}
13364
13365#[gpui::test]
13366async fn test_addition_reverts(cx: &mut TestAppContext) {
13367    init_test(cx, |_| {});
13368    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13369    let base_text = indoc! {r#"
13370        struct Row;
13371        struct Row1;
13372        struct Row2;
13373
13374        struct Row4;
13375        struct Row5;
13376        struct Row6;
13377
13378        struct Row8;
13379        struct Row9;
13380        struct Row10;"#};
13381
13382    // When addition hunks are not adjacent to carets, no hunk revert is performed
13383    assert_hunk_revert(
13384        indoc! {r#"struct Row;
13385                   struct Row1;
13386                   struct Row1.1;
13387                   struct Row1.2;
13388                   struct Row2;ˇ
13389
13390                   struct Row4;
13391                   struct Row5;
13392                   struct Row6;
13393
13394                   struct Row8;
13395                   ˇstruct Row9;
13396                   struct Row9.1;
13397                   struct Row9.2;
13398                   struct Row9.3;
13399                   struct Row10;"#},
13400        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13401        indoc! {r#"struct Row;
13402                   struct Row1;
13403                   struct Row1.1;
13404                   struct Row1.2;
13405                   struct Row2;ˇ
13406
13407                   struct Row4;
13408                   struct Row5;
13409                   struct Row6;
13410
13411                   struct Row8;
13412                   ˇstruct Row9;
13413                   struct Row9.1;
13414                   struct Row9.2;
13415                   struct Row9.3;
13416                   struct Row10;"#},
13417        base_text,
13418        &mut cx,
13419    );
13420    // Same for selections
13421    assert_hunk_revert(
13422        indoc! {r#"struct Row;
13423                   struct Row1;
13424                   struct Row2;
13425                   struct Row2.1;
13426                   struct Row2.2;
13427                   «ˇ
13428                   struct Row4;
13429                   struct» Row5;
13430                   «struct Row6;
13431                   ˇ»
13432                   struct Row9.1;
13433                   struct Row9.2;
13434                   struct Row9.3;
13435                   struct Row8;
13436                   struct Row9;
13437                   struct Row10;"#},
13438        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13439        indoc! {r#"struct Row;
13440                   struct Row1;
13441                   struct Row2;
13442                   struct Row2.1;
13443                   struct Row2.2;
13444                   «ˇ
13445                   struct Row4;
13446                   struct» Row5;
13447                   «struct Row6;
13448                   ˇ»
13449                   struct Row9.1;
13450                   struct Row9.2;
13451                   struct Row9.3;
13452                   struct Row8;
13453                   struct Row9;
13454                   struct Row10;"#},
13455        base_text,
13456        &mut cx,
13457    );
13458
13459    // When carets and selections intersect the addition hunks, those are reverted.
13460    // Adjacent carets got merged.
13461    assert_hunk_revert(
13462        indoc! {r#"struct Row;
13463                   ˇ// something on the top
13464                   struct Row1;
13465                   struct Row2;
13466                   struct Roˇw3.1;
13467                   struct Row2.2;
13468                   struct Row2.3;ˇ
13469
13470                   struct Row4;
13471                   struct ˇRow5.1;
13472                   struct Row5.2;
13473                   struct «Rowˇ»5.3;
13474                   struct Row5;
13475                   struct Row6;
13476                   ˇ
13477                   struct Row9.1;
13478                   struct «Rowˇ»9.2;
13479                   struct «ˇRow»9.3;
13480                   struct Row8;
13481                   struct Row9;
13482                   «ˇ// something on bottom»
13483                   struct Row10;"#},
13484        vec![
13485            DiffHunkStatusKind::Added,
13486            DiffHunkStatusKind::Added,
13487            DiffHunkStatusKind::Added,
13488            DiffHunkStatusKind::Added,
13489            DiffHunkStatusKind::Added,
13490        ],
13491        indoc! {r#"struct Row;
13492                   ˇstruct Row1;
13493                   struct Row2;
13494                   ˇ
13495                   struct Row4;
13496                   ˇstruct Row5;
13497                   struct Row6;
13498                   ˇ
13499                   ˇstruct Row8;
13500                   struct Row9;
13501                   ˇstruct Row10;"#},
13502        base_text,
13503        &mut cx,
13504    );
13505}
13506
13507#[gpui::test]
13508async fn test_modification_reverts(cx: &mut TestAppContext) {
13509    init_test(cx, |_| {});
13510    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13511    let base_text = indoc! {r#"
13512        struct Row;
13513        struct Row1;
13514        struct Row2;
13515
13516        struct Row4;
13517        struct Row5;
13518        struct Row6;
13519
13520        struct Row8;
13521        struct Row9;
13522        struct Row10;"#};
13523
13524    // Modification hunks behave the same as the addition ones.
13525    assert_hunk_revert(
13526        indoc! {r#"struct Row;
13527                   struct Row1;
13528                   struct Row33;
13529                   ˇ
13530                   struct Row4;
13531                   struct Row5;
13532                   struct Row6;
13533                   ˇ
13534                   struct Row99;
13535                   struct Row9;
13536                   struct Row10;"#},
13537        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13538        indoc! {r#"struct Row;
13539                   struct Row1;
13540                   struct Row33;
13541                   ˇ
13542                   struct Row4;
13543                   struct Row5;
13544                   struct Row6;
13545                   ˇ
13546                   struct Row99;
13547                   struct Row9;
13548                   struct Row10;"#},
13549        base_text,
13550        &mut cx,
13551    );
13552    assert_hunk_revert(
13553        indoc! {r#"struct Row;
13554                   struct Row1;
13555                   struct Row33;
13556                   «ˇ
13557                   struct Row4;
13558                   struct» Row5;
13559                   «struct Row6;
13560                   ˇ»
13561                   struct Row99;
13562                   struct Row9;
13563                   struct Row10;"#},
13564        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13565        indoc! {r#"struct Row;
13566                   struct Row1;
13567                   struct Row33;
13568                   «ˇ
13569                   struct Row4;
13570                   struct» Row5;
13571                   «struct Row6;
13572                   ˇ»
13573                   struct Row99;
13574                   struct Row9;
13575                   struct Row10;"#},
13576        base_text,
13577        &mut cx,
13578    );
13579
13580    assert_hunk_revert(
13581        indoc! {r#"ˇstruct Row1.1;
13582                   struct Row1;
13583                   «ˇstr»uct Row22;
13584
13585                   struct ˇRow44;
13586                   struct Row5;
13587                   struct «Rˇ»ow66;ˇ
13588
13589                   «struˇ»ct Row88;
13590                   struct Row9;
13591                   struct Row1011;ˇ"#},
13592        vec![
13593            DiffHunkStatusKind::Modified,
13594            DiffHunkStatusKind::Modified,
13595            DiffHunkStatusKind::Modified,
13596            DiffHunkStatusKind::Modified,
13597            DiffHunkStatusKind::Modified,
13598            DiffHunkStatusKind::Modified,
13599        ],
13600        indoc! {r#"struct Row;
13601                   ˇstruct Row1;
13602                   struct Row2;
13603                   ˇ
13604                   struct Row4;
13605                   ˇstruct Row5;
13606                   struct Row6;
13607                   ˇ
13608                   struct Row8;
13609                   ˇstruct Row9;
13610                   struct Row10;ˇ"#},
13611        base_text,
13612        &mut cx,
13613    );
13614}
13615
13616#[gpui::test]
13617async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13618    init_test(cx, |_| {});
13619    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13620    let base_text = indoc! {r#"
13621        one
13622
13623        two
13624        three
13625        "#};
13626
13627    cx.set_head_text(base_text);
13628    cx.set_state("\nˇ\n");
13629    cx.executor().run_until_parked();
13630    cx.update_editor(|editor, _window, cx| {
13631        editor.expand_selected_diff_hunks(cx);
13632    });
13633    cx.executor().run_until_parked();
13634    cx.update_editor(|editor, window, cx| {
13635        editor.backspace(&Default::default(), window, cx);
13636    });
13637    cx.run_until_parked();
13638    cx.assert_state_with_diff(
13639        indoc! {r#"
13640
13641        - two
13642        - threeˇ
13643        +
13644        "#}
13645        .to_string(),
13646    );
13647}
13648
13649#[gpui::test]
13650async fn test_deletion_reverts(cx: &mut TestAppContext) {
13651    init_test(cx, |_| {});
13652    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13653    let base_text = indoc! {r#"struct Row;
13654struct Row1;
13655struct Row2;
13656
13657struct Row4;
13658struct Row5;
13659struct Row6;
13660
13661struct Row8;
13662struct Row9;
13663struct Row10;"#};
13664
13665    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13666    assert_hunk_revert(
13667        indoc! {r#"struct Row;
13668                   struct Row2;
13669
13670                   ˇstruct Row4;
13671                   struct Row5;
13672                   struct Row6;
13673                   ˇ
13674                   struct Row8;
13675                   struct Row10;"#},
13676        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13677        indoc! {r#"struct Row;
13678                   struct Row2;
13679
13680                   ˇstruct Row4;
13681                   struct Row5;
13682                   struct Row6;
13683                   ˇ
13684                   struct Row8;
13685                   struct Row10;"#},
13686        base_text,
13687        &mut cx,
13688    );
13689    assert_hunk_revert(
13690        indoc! {r#"struct Row;
13691                   struct Row2;
13692
13693                   «ˇstruct Row4;
13694                   struct» Row5;
13695                   «struct Row6;
13696                   ˇ»
13697                   struct Row8;
13698                   struct Row10;"#},
13699        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13700        indoc! {r#"struct Row;
13701                   struct Row2;
13702
13703                   «ˇstruct Row4;
13704                   struct» Row5;
13705                   «struct Row6;
13706                   ˇ»
13707                   struct Row8;
13708                   struct Row10;"#},
13709        base_text,
13710        &mut cx,
13711    );
13712
13713    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13714    assert_hunk_revert(
13715        indoc! {r#"struct Row;
13716                   ˇstruct Row2;
13717
13718                   struct Row4;
13719                   struct Row5;
13720                   struct Row6;
13721
13722                   struct Row8;ˇ
13723                   struct Row10;"#},
13724        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13725        indoc! {r#"struct Row;
13726                   struct Row1;
13727                   ˇstruct Row2;
13728
13729                   struct Row4;
13730                   struct Row5;
13731                   struct Row6;
13732
13733                   struct Row8;ˇ
13734                   struct Row9;
13735                   struct Row10;"#},
13736        base_text,
13737        &mut cx,
13738    );
13739    assert_hunk_revert(
13740        indoc! {r#"struct Row;
13741                   struct Row2«ˇ;
13742                   struct Row4;
13743                   struct» Row5;
13744                   «struct Row6;
13745
13746                   struct Row8;ˇ»
13747                   struct Row10;"#},
13748        vec![
13749            DiffHunkStatusKind::Deleted,
13750            DiffHunkStatusKind::Deleted,
13751            DiffHunkStatusKind::Deleted,
13752        ],
13753        indoc! {r#"struct Row;
13754                   struct Row1;
13755                   struct Row2«ˇ;
13756
13757                   struct Row4;
13758                   struct» Row5;
13759                   «struct Row6;
13760
13761                   struct Row8;ˇ»
13762                   struct Row9;
13763                   struct Row10;"#},
13764        base_text,
13765        &mut cx,
13766    );
13767}
13768
13769#[gpui::test]
13770async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13771    init_test(cx, |_| {});
13772
13773    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13774    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13775    let base_text_3 =
13776        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13777
13778    let text_1 = edit_first_char_of_every_line(base_text_1);
13779    let text_2 = edit_first_char_of_every_line(base_text_2);
13780    let text_3 = edit_first_char_of_every_line(base_text_3);
13781
13782    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13783    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13784    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13785
13786    let multibuffer = cx.new(|cx| {
13787        let mut multibuffer = MultiBuffer::new(ReadWrite);
13788        multibuffer.push_excerpts(
13789            buffer_1.clone(),
13790            [
13791                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13792                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13793                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13794            ],
13795            cx,
13796        );
13797        multibuffer.push_excerpts(
13798            buffer_2.clone(),
13799            [
13800                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13801                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13802                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13803            ],
13804            cx,
13805        );
13806        multibuffer.push_excerpts(
13807            buffer_3.clone(),
13808            [
13809                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13810                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13811                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13812            ],
13813            cx,
13814        );
13815        multibuffer
13816    });
13817
13818    let fs = FakeFs::new(cx.executor());
13819    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13820    let (editor, cx) = cx
13821        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13822    editor.update_in(cx, |editor, _window, cx| {
13823        for (buffer, diff_base) in [
13824            (buffer_1.clone(), base_text_1),
13825            (buffer_2.clone(), base_text_2),
13826            (buffer_3.clone(), base_text_3),
13827        ] {
13828            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13829            editor
13830                .buffer
13831                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13832        }
13833    });
13834    cx.executor().run_until_parked();
13835
13836    editor.update_in(cx, |editor, window, cx| {
13837        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}");
13838        editor.select_all(&SelectAll, window, cx);
13839        editor.git_restore(&Default::default(), window, cx);
13840    });
13841    cx.executor().run_until_parked();
13842
13843    // When all ranges are selected, all buffer hunks are reverted.
13844    editor.update(cx, |editor, cx| {
13845        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");
13846    });
13847    buffer_1.update(cx, |buffer, _| {
13848        assert_eq!(buffer.text(), base_text_1);
13849    });
13850    buffer_2.update(cx, |buffer, _| {
13851        assert_eq!(buffer.text(), base_text_2);
13852    });
13853    buffer_3.update(cx, |buffer, _| {
13854        assert_eq!(buffer.text(), base_text_3);
13855    });
13856
13857    editor.update_in(cx, |editor, window, cx| {
13858        editor.undo(&Default::default(), window, cx);
13859    });
13860
13861    editor.update_in(cx, |editor, window, cx| {
13862        editor.change_selections(None, window, cx, |s| {
13863            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13864        });
13865        editor.git_restore(&Default::default(), window, cx);
13866    });
13867
13868    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13869    // but not affect buffer_2 and its related excerpts.
13870    editor.update(cx, |editor, cx| {
13871        assert_eq!(
13872            editor.text(cx),
13873            "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}"
13874        );
13875    });
13876    buffer_1.update(cx, |buffer, _| {
13877        assert_eq!(buffer.text(), base_text_1);
13878    });
13879    buffer_2.update(cx, |buffer, _| {
13880        assert_eq!(
13881            buffer.text(),
13882            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13883        );
13884    });
13885    buffer_3.update(cx, |buffer, _| {
13886        assert_eq!(
13887            buffer.text(),
13888            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13889        );
13890    });
13891
13892    fn edit_first_char_of_every_line(text: &str) -> String {
13893        text.split('\n')
13894            .map(|line| format!("X{}", &line[1..]))
13895            .collect::<Vec<_>>()
13896            .join("\n")
13897    }
13898}
13899
13900#[gpui::test]
13901async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13902    init_test(cx, |_| {});
13903
13904    let cols = 4;
13905    let rows = 10;
13906    let sample_text_1 = sample_text(rows, cols, 'a');
13907    assert_eq!(
13908        sample_text_1,
13909        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13910    );
13911    let sample_text_2 = sample_text(rows, cols, 'l');
13912    assert_eq!(
13913        sample_text_2,
13914        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13915    );
13916    let sample_text_3 = sample_text(rows, cols, 'v');
13917    assert_eq!(
13918        sample_text_3,
13919        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13920    );
13921
13922    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13923    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13924    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13925
13926    let multi_buffer = cx.new(|cx| {
13927        let mut multibuffer = MultiBuffer::new(ReadWrite);
13928        multibuffer.push_excerpts(
13929            buffer_1.clone(),
13930            [
13931                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13932                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13933                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13934            ],
13935            cx,
13936        );
13937        multibuffer.push_excerpts(
13938            buffer_2.clone(),
13939            [
13940                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13941                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13942                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13943            ],
13944            cx,
13945        );
13946        multibuffer.push_excerpts(
13947            buffer_3.clone(),
13948            [
13949                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13950                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13951                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13952            ],
13953            cx,
13954        );
13955        multibuffer
13956    });
13957
13958    let fs = FakeFs::new(cx.executor());
13959    fs.insert_tree(
13960        "/a",
13961        json!({
13962            "main.rs": sample_text_1,
13963            "other.rs": sample_text_2,
13964            "lib.rs": sample_text_3,
13965        }),
13966    )
13967    .await;
13968    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13969    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13970    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13971    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13972        Editor::new(
13973            EditorMode::Full,
13974            multi_buffer,
13975            Some(project.clone()),
13976            window,
13977            cx,
13978        )
13979    });
13980    let multibuffer_item_id = workspace
13981        .update(cx, |workspace, window, cx| {
13982            assert!(
13983                workspace.active_item(cx).is_none(),
13984                "active item should be None before the first item is added"
13985            );
13986            workspace.add_item_to_active_pane(
13987                Box::new(multi_buffer_editor.clone()),
13988                None,
13989                true,
13990                window,
13991                cx,
13992            );
13993            let active_item = workspace
13994                .active_item(cx)
13995                .expect("should have an active item after adding the multi buffer");
13996            assert!(
13997                !active_item.is_singleton(cx),
13998                "A multi buffer was expected to active after adding"
13999            );
14000            active_item.item_id()
14001        })
14002        .unwrap();
14003    cx.executor().run_until_parked();
14004
14005    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14006        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14007            s.select_ranges(Some(1..2))
14008        });
14009        editor.open_excerpts(&OpenExcerpts, window, cx);
14010    });
14011    cx.executor().run_until_parked();
14012    let first_item_id = workspace
14013        .update(cx, |workspace, window, cx| {
14014            let active_item = workspace
14015                .active_item(cx)
14016                .expect("should have an active item after navigating into the 1st buffer");
14017            let first_item_id = active_item.item_id();
14018            assert_ne!(
14019                first_item_id, multibuffer_item_id,
14020                "Should navigate into the 1st buffer and activate it"
14021            );
14022            assert!(
14023                active_item.is_singleton(cx),
14024                "New active item should be a singleton buffer"
14025            );
14026            assert_eq!(
14027                active_item
14028                    .act_as::<Editor>(cx)
14029                    .expect("should have navigated into an editor for the 1st buffer")
14030                    .read(cx)
14031                    .text(cx),
14032                sample_text_1
14033            );
14034
14035            workspace
14036                .go_back(workspace.active_pane().downgrade(), window, cx)
14037                .detach_and_log_err(cx);
14038
14039            first_item_id
14040        })
14041        .unwrap();
14042    cx.executor().run_until_parked();
14043    workspace
14044        .update(cx, |workspace, _, cx| {
14045            let active_item = workspace
14046                .active_item(cx)
14047                .expect("should have an active item after navigating back");
14048            assert_eq!(
14049                active_item.item_id(),
14050                multibuffer_item_id,
14051                "Should navigate back to the multi buffer"
14052            );
14053            assert!(!active_item.is_singleton(cx));
14054        })
14055        .unwrap();
14056
14057    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14058        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14059            s.select_ranges(Some(39..40))
14060        });
14061        editor.open_excerpts(&OpenExcerpts, window, cx);
14062    });
14063    cx.executor().run_until_parked();
14064    let second_item_id = workspace
14065        .update(cx, |workspace, window, cx| {
14066            let active_item = workspace
14067                .active_item(cx)
14068                .expect("should have an active item after navigating into the 2nd buffer");
14069            let second_item_id = active_item.item_id();
14070            assert_ne!(
14071                second_item_id, multibuffer_item_id,
14072                "Should navigate away from the multibuffer"
14073            );
14074            assert_ne!(
14075                second_item_id, first_item_id,
14076                "Should navigate into the 2nd buffer and activate it"
14077            );
14078            assert!(
14079                active_item.is_singleton(cx),
14080                "New active item should be a singleton buffer"
14081            );
14082            assert_eq!(
14083                active_item
14084                    .act_as::<Editor>(cx)
14085                    .expect("should have navigated into an editor")
14086                    .read(cx)
14087                    .text(cx),
14088                sample_text_2
14089            );
14090
14091            workspace
14092                .go_back(workspace.active_pane().downgrade(), window, cx)
14093                .detach_and_log_err(cx);
14094
14095            second_item_id
14096        })
14097        .unwrap();
14098    cx.executor().run_until_parked();
14099    workspace
14100        .update(cx, |workspace, _, cx| {
14101            let active_item = workspace
14102                .active_item(cx)
14103                .expect("should have an active item after navigating back from the 2nd buffer");
14104            assert_eq!(
14105                active_item.item_id(),
14106                multibuffer_item_id,
14107                "Should navigate back from the 2nd buffer to the multi buffer"
14108            );
14109            assert!(!active_item.is_singleton(cx));
14110        })
14111        .unwrap();
14112
14113    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14114        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14115            s.select_ranges(Some(70..70))
14116        });
14117        editor.open_excerpts(&OpenExcerpts, window, cx);
14118    });
14119    cx.executor().run_until_parked();
14120    workspace
14121        .update(cx, |workspace, window, cx| {
14122            let active_item = workspace
14123                .active_item(cx)
14124                .expect("should have an active item after navigating into the 3rd buffer");
14125            let third_item_id = active_item.item_id();
14126            assert_ne!(
14127                third_item_id, multibuffer_item_id,
14128                "Should navigate into the 3rd buffer and activate it"
14129            );
14130            assert_ne!(third_item_id, first_item_id);
14131            assert_ne!(third_item_id, second_item_id);
14132            assert!(
14133                active_item.is_singleton(cx),
14134                "New active item should be a singleton buffer"
14135            );
14136            assert_eq!(
14137                active_item
14138                    .act_as::<Editor>(cx)
14139                    .expect("should have navigated into an editor")
14140                    .read(cx)
14141                    .text(cx),
14142                sample_text_3
14143            );
14144
14145            workspace
14146                .go_back(workspace.active_pane().downgrade(), window, cx)
14147                .detach_and_log_err(cx);
14148        })
14149        .unwrap();
14150    cx.executor().run_until_parked();
14151    workspace
14152        .update(cx, |workspace, _, cx| {
14153            let active_item = workspace
14154                .active_item(cx)
14155                .expect("should have an active item after navigating back from the 3rd buffer");
14156            assert_eq!(
14157                active_item.item_id(),
14158                multibuffer_item_id,
14159                "Should navigate back from the 3rd buffer to the multi buffer"
14160            );
14161            assert!(!active_item.is_singleton(cx));
14162        })
14163        .unwrap();
14164}
14165
14166#[gpui::test]
14167async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14168    init_test(cx, |_| {});
14169
14170    let mut cx = EditorTestContext::new(cx).await;
14171
14172    let diff_base = r#"
14173        use some::mod;
14174
14175        const A: u32 = 42;
14176
14177        fn main() {
14178            println!("hello");
14179
14180            println!("world");
14181        }
14182        "#
14183    .unindent();
14184
14185    cx.set_state(
14186        &r#"
14187        use some::modified;
14188
14189        ˇ
14190        fn main() {
14191            println!("hello there");
14192
14193            println!("around the");
14194            println!("world");
14195        }
14196        "#
14197        .unindent(),
14198    );
14199
14200    cx.set_head_text(&diff_base);
14201    executor.run_until_parked();
14202
14203    cx.update_editor(|editor, window, cx| {
14204        editor.go_to_next_hunk(&GoToHunk, window, cx);
14205        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14206    });
14207    executor.run_until_parked();
14208    cx.assert_state_with_diff(
14209        r#"
14210          use some::modified;
14211
14212
14213          fn main() {
14214        -     println!("hello");
14215        + ˇ    println!("hello there");
14216
14217              println!("around the");
14218              println!("world");
14219          }
14220        "#
14221        .unindent(),
14222    );
14223
14224    cx.update_editor(|editor, window, cx| {
14225        for _ in 0..2 {
14226            editor.go_to_next_hunk(&GoToHunk, window, cx);
14227            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14228        }
14229    });
14230    executor.run_until_parked();
14231    cx.assert_state_with_diff(
14232        r#"
14233        - use some::mod;
14234        + ˇuse some::modified;
14235
14236
14237          fn main() {
14238        -     println!("hello");
14239        +     println!("hello there");
14240
14241        +     println!("around the");
14242              println!("world");
14243          }
14244        "#
14245        .unindent(),
14246    );
14247
14248    cx.update_editor(|editor, window, cx| {
14249        editor.go_to_next_hunk(&GoToHunk, window, cx);
14250        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14251    });
14252    executor.run_until_parked();
14253    cx.assert_state_with_diff(
14254        r#"
14255        - use some::mod;
14256        + use some::modified;
14257
14258        - const A: u32 = 42;
14259          ˇ
14260          fn main() {
14261        -     println!("hello");
14262        +     println!("hello there");
14263
14264        +     println!("around the");
14265              println!("world");
14266          }
14267        "#
14268        .unindent(),
14269    );
14270
14271    cx.update_editor(|editor, window, cx| {
14272        editor.cancel(&Cancel, window, cx);
14273    });
14274
14275    cx.assert_state_with_diff(
14276        r#"
14277          use some::modified;
14278
14279          ˇ
14280          fn main() {
14281              println!("hello there");
14282
14283              println!("around the");
14284              println!("world");
14285          }
14286        "#
14287        .unindent(),
14288    );
14289}
14290
14291#[gpui::test]
14292async fn test_diff_base_change_with_expanded_diff_hunks(
14293    executor: BackgroundExecutor,
14294    cx: &mut TestAppContext,
14295) {
14296    init_test(cx, |_| {});
14297
14298    let mut cx = EditorTestContext::new(cx).await;
14299
14300    let diff_base = r#"
14301        use some::mod1;
14302        use some::mod2;
14303
14304        const A: u32 = 42;
14305        const B: u32 = 42;
14306        const C: u32 = 42;
14307
14308        fn main() {
14309            println!("hello");
14310
14311            println!("world");
14312        }
14313        "#
14314    .unindent();
14315
14316    cx.set_state(
14317        &r#"
14318        use some::mod2;
14319
14320        const A: u32 = 42;
14321        const C: u32 = 42;
14322
14323        fn main(ˇ) {
14324            //println!("hello");
14325
14326            println!("world");
14327            //
14328            //
14329        }
14330        "#
14331        .unindent(),
14332    );
14333
14334    cx.set_head_text(&diff_base);
14335    executor.run_until_parked();
14336
14337    cx.update_editor(|editor, window, cx| {
14338        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14339    });
14340    executor.run_until_parked();
14341    cx.assert_state_with_diff(
14342        r#"
14343        - use some::mod1;
14344          use some::mod2;
14345
14346          const A: u32 = 42;
14347        - const B: u32 = 42;
14348          const C: u32 = 42;
14349
14350          fn main(ˇ) {
14351        -     println!("hello");
14352        +     //println!("hello");
14353
14354              println!("world");
14355        +     //
14356        +     //
14357          }
14358        "#
14359        .unindent(),
14360    );
14361
14362    cx.set_head_text("new diff base!");
14363    executor.run_until_parked();
14364    cx.assert_state_with_diff(
14365        r#"
14366        - new diff base!
14367        + use some::mod2;
14368        +
14369        + const A: u32 = 42;
14370        + const C: u32 = 42;
14371        +
14372        + fn main(ˇ) {
14373        +     //println!("hello");
14374        +
14375        +     println!("world");
14376        +     //
14377        +     //
14378        + }
14379        "#
14380        .unindent(),
14381    );
14382}
14383
14384#[gpui::test]
14385async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14386    init_test(cx, |_| {});
14387
14388    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14389    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14390    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14391    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14392    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14393    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14394
14395    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14396    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14397    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14398
14399    let multi_buffer = cx.new(|cx| {
14400        let mut multibuffer = MultiBuffer::new(ReadWrite);
14401        multibuffer.push_excerpts(
14402            buffer_1.clone(),
14403            [
14404                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14405                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14406                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14407            ],
14408            cx,
14409        );
14410        multibuffer.push_excerpts(
14411            buffer_2.clone(),
14412            [
14413                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14414                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14415                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14416            ],
14417            cx,
14418        );
14419        multibuffer.push_excerpts(
14420            buffer_3.clone(),
14421            [
14422                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14423                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14424                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14425            ],
14426            cx,
14427        );
14428        multibuffer
14429    });
14430
14431    let editor =
14432        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14433    editor
14434        .update(cx, |editor, _window, cx| {
14435            for (buffer, diff_base) in [
14436                (buffer_1.clone(), file_1_old),
14437                (buffer_2.clone(), file_2_old),
14438                (buffer_3.clone(), file_3_old),
14439            ] {
14440                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14441                editor
14442                    .buffer
14443                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14444            }
14445        })
14446        .unwrap();
14447
14448    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14449    cx.run_until_parked();
14450
14451    cx.assert_editor_state(
14452        &"
14453            ˇaaa
14454            ccc
14455            ddd
14456
14457            ggg
14458            hhh
14459
14460
14461            lll
14462            mmm
14463            NNN
14464
14465            qqq
14466            rrr
14467
14468            uuu
14469            111
14470            222
14471            333
14472
14473            666
14474            777
14475
14476            000
14477            !!!"
14478        .unindent(),
14479    );
14480
14481    cx.update_editor(|editor, window, cx| {
14482        editor.select_all(&SelectAll, window, cx);
14483        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14484    });
14485    cx.executor().run_until_parked();
14486
14487    cx.assert_state_with_diff(
14488        "
14489            «aaa
14490          - bbb
14491            ccc
14492            ddd
14493
14494            ggg
14495            hhh
14496
14497
14498            lll
14499            mmm
14500          - nnn
14501          + NNN
14502
14503            qqq
14504            rrr
14505
14506            uuu
14507            111
14508            222
14509            333
14510
14511          + 666
14512            777
14513
14514            000
14515            !!!ˇ»"
14516            .unindent(),
14517    );
14518}
14519
14520#[gpui::test]
14521async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14522    init_test(cx, |_| {});
14523
14524    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14525    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14526
14527    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14528    let multi_buffer = cx.new(|cx| {
14529        let mut multibuffer = MultiBuffer::new(ReadWrite);
14530        multibuffer.push_excerpts(
14531            buffer.clone(),
14532            [
14533                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14534                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14535                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14536            ],
14537            cx,
14538        );
14539        multibuffer
14540    });
14541
14542    let editor =
14543        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14544    editor
14545        .update(cx, |editor, _window, cx| {
14546            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14547            editor
14548                .buffer
14549                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14550        })
14551        .unwrap();
14552
14553    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14554    cx.run_until_parked();
14555
14556    cx.update_editor(|editor, window, cx| {
14557        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14558    });
14559    cx.executor().run_until_parked();
14560
14561    // When the start of a hunk coincides with the start of its excerpt,
14562    // the hunk is expanded. When the start of a a hunk is earlier than
14563    // the start of its excerpt, the hunk is not expanded.
14564    cx.assert_state_with_diff(
14565        "
14566            ˇaaa
14567          - bbb
14568          + BBB
14569
14570          - ddd
14571          - eee
14572          + DDD
14573          + EEE
14574            fff
14575
14576            iii
14577        "
14578        .unindent(),
14579    );
14580}
14581
14582#[gpui::test]
14583async fn test_edits_around_expanded_insertion_hunks(
14584    executor: BackgroundExecutor,
14585    cx: &mut TestAppContext,
14586) {
14587    init_test(cx, |_| {});
14588
14589    let mut cx = EditorTestContext::new(cx).await;
14590
14591    let diff_base = r#"
14592        use some::mod1;
14593        use some::mod2;
14594
14595        const A: u32 = 42;
14596
14597        fn main() {
14598            println!("hello");
14599
14600            println!("world");
14601        }
14602        "#
14603    .unindent();
14604    executor.run_until_parked();
14605    cx.set_state(
14606        &r#"
14607        use some::mod1;
14608        use some::mod2;
14609
14610        const A: u32 = 42;
14611        const B: u32 = 42;
14612        const C: u32 = 42;
14613        ˇ
14614
14615        fn main() {
14616            println!("hello");
14617
14618            println!("world");
14619        }
14620        "#
14621        .unindent(),
14622    );
14623
14624    cx.set_head_text(&diff_base);
14625    executor.run_until_parked();
14626
14627    cx.update_editor(|editor, window, cx| {
14628        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14629    });
14630    executor.run_until_parked();
14631
14632    cx.assert_state_with_diff(
14633        r#"
14634        use some::mod1;
14635        use some::mod2;
14636
14637        const A: u32 = 42;
14638      + const B: u32 = 42;
14639      + const C: u32 = 42;
14640      + ˇ
14641
14642        fn main() {
14643            println!("hello");
14644
14645            println!("world");
14646        }
14647      "#
14648        .unindent(),
14649    );
14650
14651    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14652    executor.run_until_parked();
14653
14654    cx.assert_state_with_diff(
14655        r#"
14656        use some::mod1;
14657        use some::mod2;
14658
14659        const A: u32 = 42;
14660      + const B: u32 = 42;
14661      + const C: u32 = 42;
14662      + const D: u32 = 42;
14663      + ˇ
14664
14665        fn main() {
14666            println!("hello");
14667
14668            println!("world");
14669        }
14670      "#
14671        .unindent(),
14672    );
14673
14674    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14675    executor.run_until_parked();
14676
14677    cx.assert_state_with_diff(
14678        r#"
14679        use some::mod1;
14680        use some::mod2;
14681
14682        const A: u32 = 42;
14683      + const B: u32 = 42;
14684      + const C: u32 = 42;
14685      + const D: u32 = 42;
14686      + const E: u32 = 42;
14687      + ˇ
14688
14689        fn main() {
14690            println!("hello");
14691
14692            println!("world");
14693        }
14694      "#
14695        .unindent(),
14696    );
14697
14698    cx.update_editor(|editor, window, cx| {
14699        editor.delete_line(&DeleteLine, window, cx);
14700    });
14701    executor.run_until_parked();
14702
14703    cx.assert_state_with_diff(
14704        r#"
14705        use some::mod1;
14706        use some::mod2;
14707
14708        const A: u32 = 42;
14709      + const B: u32 = 42;
14710      + const C: u32 = 42;
14711      + const D: u32 = 42;
14712      + const E: u32 = 42;
14713        ˇ
14714        fn main() {
14715            println!("hello");
14716
14717            println!("world");
14718        }
14719      "#
14720        .unindent(),
14721    );
14722
14723    cx.update_editor(|editor, window, cx| {
14724        editor.move_up(&MoveUp, window, cx);
14725        editor.delete_line(&DeleteLine, window, cx);
14726        editor.move_up(&MoveUp, window, cx);
14727        editor.delete_line(&DeleteLine, window, cx);
14728        editor.move_up(&MoveUp, window, cx);
14729        editor.delete_line(&DeleteLine, window, cx);
14730    });
14731    executor.run_until_parked();
14732    cx.assert_state_with_diff(
14733        r#"
14734        use some::mod1;
14735        use some::mod2;
14736
14737        const A: u32 = 42;
14738      + const B: u32 = 42;
14739        ˇ
14740        fn main() {
14741            println!("hello");
14742
14743            println!("world");
14744        }
14745      "#
14746        .unindent(),
14747    );
14748
14749    cx.update_editor(|editor, window, cx| {
14750        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14751        editor.delete_line(&DeleteLine, window, cx);
14752    });
14753    executor.run_until_parked();
14754    cx.assert_state_with_diff(
14755        r#"
14756        ˇ
14757        fn main() {
14758            println!("hello");
14759
14760            println!("world");
14761        }
14762      "#
14763        .unindent(),
14764    );
14765}
14766
14767#[gpui::test]
14768async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14769    init_test(cx, |_| {});
14770
14771    let mut cx = EditorTestContext::new(cx).await;
14772    cx.set_head_text(indoc! { "
14773        one
14774        two
14775        three
14776        four
14777        five
14778        "
14779    });
14780    cx.set_state(indoc! { "
14781        one
14782        ˇthree
14783        five
14784    "});
14785    cx.run_until_parked();
14786    cx.update_editor(|editor, window, cx| {
14787        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14788    });
14789    cx.assert_state_with_diff(
14790        indoc! { "
14791        one
14792      - two
14793        ˇthree
14794      - four
14795        five
14796    "}
14797        .to_string(),
14798    );
14799    cx.update_editor(|editor, window, cx| {
14800        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14801    });
14802
14803    cx.assert_state_with_diff(
14804        indoc! { "
14805        one
14806        ˇthree
14807        five
14808    "}
14809        .to_string(),
14810    );
14811
14812    cx.set_state(indoc! { "
14813        one
14814        ˇTWO
14815        three
14816        four
14817        five
14818    "});
14819    cx.run_until_parked();
14820    cx.update_editor(|editor, window, cx| {
14821        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14822    });
14823
14824    cx.assert_state_with_diff(
14825        indoc! { "
14826            one
14827          - two
14828          + ˇTWO
14829            three
14830            four
14831            five
14832        "}
14833        .to_string(),
14834    );
14835    cx.update_editor(|editor, window, cx| {
14836        editor.move_up(&Default::default(), window, cx);
14837        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14838    });
14839    cx.assert_state_with_diff(
14840        indoc! { "
14841            one
14842            ˇTWO
14843            three
14844            four
14845            five
14846        "}
14847        .to_string(),
14848    );
14849}
14850
14851#[gpui::test]
14852async fn test_edits_around_expanded_deletion_hunks(
14853    executor: BackgroundExecutor,
14854    cx: &mut TestAppContext,
14855) {
14856    init_test(cx, |_| {});
14857
14858    let mut cx = EditorTestContext::new(cx).await;
14859
14860    let diff_base = r#"
14861        use some::mod1;
14862        use some::mod2;
14863
14864        const A: u32 = 42;
14865        const B: u32 = 42;
14866        const C: u32 = 42;
14867
14868
14869        fn main() {
14870            println!("hello");
14871
14872            println!("world");
14873        }
14874    "#
14875    .unindent();
14876    executor.run_until_parked();
14877    cx.set_state(
14878        &r#"
14879        use some::mod1;
14880        use some::mod2;
14881
14882        ˇconst B: u32 = 42;
14883        const C: u32 = 42;
14884
14885
14886        fn main() {
14887            println!("hello");
14888
14889            println!("world");
14890        }
14891        "#
14892        .unindent(),
14893    );
14894
14895    cx.set_head_text(&diff_base);
14896    executor.run_until_parked();
14897
14898    cx.update_editor(|editor, window, cx| {
14899        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14900    });
14901    executor.run_until_parked();
14902
14903    cx.assert_state_with_diff(
14904        r#"
14905        use some::mod1;
14906        use some::mod2;
14907
14908      - const A: u32 = 42;
14909        ˇconst B: u32 = 42;
14910        const C: u32 = 42;
14911
14912
14913        fn main() {
14914            println!("hello");
14915
14916            println!("world");
14917        }
14918      "#
14919        .unindent(),
14920    );
14921
14922    cx.update_editor(|editor, window, cx| {
14923        editor.delete_line(&DeleteLine, window, cx);
14924    });
14925    executor.run_until_parked();
14926    cx.assert_state_with_diff(
14927        r#"
14928        use some::mod1;
14929        use some::mod2;
14930
14931      - const A: u32 = 42;
14932      - const B: u32 = 42;
14933        ˇconst C: u32 = 42;
14934
14935
14936        fn main() {
14937            println!("hello");
14938
14939            println!("world");
14940        }
14941      "#
14942        .unindent(),
14943    );
14944
14945    cx.update_editor(|editor, window, cx| {
14946        editor.delete_line(&DeleteLine, window, cx);
14947    });
14948    executor.run_until_parked();
14949    cx.assert_state_with_diff(
14950        r#"
14951        use some::mod1;
14952        use some::mod2;
14953
14954      - const A: u32 = 42;
14955      - const B: u32 = 42;
14956      - const C: u32 = 42;
14957        ˇ
14958
14959        fn main() {
14960            println!("hello");
14961
14962            println!("world");
14963        }
14964      "#
14965        .unindent(),
14966    );
14967
14968    cx.update_editor(|editor, window, cx| {
14969        editor.handle_input("replacement", window, cx);
14970    });
14971    executor.run_until_parked();
14972    cx.assert_state_with_diff(
14973        r#"
14974        use some::mod1;
14975        use some::mod2;
14976
14977      - const A: u32 = 42;
14978      - const B: u32 = 42;
14979      - const C: u32 = 42;
14980      -
14981      + replacementˇ
14982
14983        fn main() {
14984            println!("hello");
14985
14986            println!("world");
14987        }
14988      "#
14989        .unindent(),
14990    );
14991}
14992
14993#[gpui::test]
14994async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14995    init_test(cx, |_| {});
14996
14997    let mut cx = EditorTestContext::new(cx).await;
14998
14999    let base_text = r#"
15000        one
15001        two
15002        three
15003        four
15004        five
15005    "#
15006    .unindent();
15007    executor.run_until_parked();
15008    cx.set_state(
15009        &r#"
15010        one
15011        two
15012        fˇour
15013        five
15014        "#
15015        .unindent(),
15016    );
15017
15018    cx.set_head_text(&base_text);
15019    executor.run_until_parked();
15020
15021    cx.update_editor(|editor, window, cx| {
15022        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15023    });
15024    executor.run_until_parked();
15025
15026    cx.assert_state_with_diff(
15027        r#"
15028          one
15029          two
15030        - three
15031          fˇour
15032          five
15033        "#
15034        .unindent(),
15035    );
15036
15037    cx.update_editor(|editor, window, cx| {
15038        editor.backspace(&Backspace, window, cx);
15039        editor.backspace(&Backspace, window, cx);
15040    });
15041    executor.run_until_parked();
15042    cx.assert_state_with_diff(
15043        r#"
15044          one
15045          two
15046        - threeˇ
15047        - four
15048        + our
15049          five
15050        "#
15051        .unindent(),
15052    );
15053}
15054
15055#[gpui::test]
15056async fn test_edit_after_expanded_modification_hunk(
15057    executor: BackgroundExecutor,
15058    cx: &mut TestAppContext,
15059) {
15060    init_test(cx, |_| {});
15061
15062    let mut cx = EditorTestContext::new(cx).await;
15063
15064    let diff_base = r#"
15065        use some::mod1;
15066        use some::mod2;
15067
15068        const A: u32 = 42;
15069        const B: u32 = 42;
15070        const C: u32 = 42;
15071        const D: u32 = 42;
15072
15073
15074        fn main() {
15075            println!("hello");
15076
15077            println!("world");
15078        }"#
15079    .unindent();
15080
15081    cx.set_state(
15082        &r#"
15083        use some::mod1;
15084        use some::mod2;
15085
15086        const A: u32 = 42;
15087        const B: u32 = 42;
15088        const C: u32 = 43ˇ
15089        const D: u32 = 42;
15090
15091
15092        fn main() {
15093            println!("hello");
15094
15095            println!("world");
15096        }"#
15097        .unindent(),
15098    );
15099
15100    cx.set_head_text(&diff_base);
15101    executor.run_until_parked();
15102    cx.update_editor(|editor, window, cx| {
15103        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15104    });
15105    executor.run_until_parked();
15106
15107    cx.assert_state_with_diff(
15108        r#"
15109        use some::mod1;
15110        use some::mod2;
15111
15112        const A: u32 = 42;
15113        const B: u32 = 42;
15114      - const C: u32 = 42;
15115      + const C: u32 = 43ˇ
15116        const D: u32 = 42;
15117
15118
15119        fn main() {
15120            println!("hello");
15121
15122            println!("world");
15123        }"#
15124        .unindent(),
15125    );
15126
15127    cx.update_editor(|editor, window, cx| {
15128        editor.handle_input("\nnew_line\n", window, cx);
15129    });
15130    executor.run_until_parked();
15131
15132    cx.assert_state_with_diff(
15133        r#"
15134        use some::mod1;
15135        use some::mod2;
15136
15137        const A: u32 = 42;
15138        const B: u32 = 42;
15139      - const C: u32 = 42;
15140      + const C: u32 = 43
15141      + new_line
15142      + ˇ
15143        const D: u32 = 42;
15144
15145
15146        fn main() {
15147            println!("hello");
15148
15149            println!("world");
15150        }"#
15151        .unindent(),
15152    );
15153}
15154
15155#[gpui::test]
15156async fn test_stage_and_unstage_added_file_hunk(
15157    executor: BackgroundExecutor,
15158    cx: &mut TestAppContext,
15159) {
15160    init_test(cx, |_| {});
15161
15162    let mut cx = EditorTestContext::new(cx).await;
15163    cx.update_editor(|editor, _, cx| {
15164        editor.set_expand_all_diff_hunks(cx);
15165    });
15166
15167    let working_copy = r#"
15168            ˇfn main() {
15169                println!("hello, world!");
15170            }
15171        "#
15172    .unindent();
15173
15174    cx.set_state(&working_copy);
15175    executor.run_until_parked();
15176
15177    cx.assert_state_with_diff(
15178        r#"
15179            + ˇfn main() {
15180            +     println!("hello, world!");
15181            + }
15182        "#
15183        .unindent(),
15184    );
15185    cx.assert_index_text(None);
15186
15187    cx.update_editor(|editor, window, cx| {
15188        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15189    });
15190    executor.run_until_parked();
15191    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15192    cx.assert_state_with_diff(
15193        r#"
15194            + ˇfn main() {
15195            +     println!("hello, world!");
15196            + }
15197        "#
15198        .unindent(),
15199    );
15200
15201    cx.update_editor(|editor, window, cx| {
15202        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15203    });
15204    executor.run_until_parked();
15205    cx.assert_index_text(None);
15206}
15207
15208async fn setup_indent_guides_editor(
15209    text: &str,
15210    cx: &mut TestAppContext,
15211) -> (BufferId, EditorTestContext) {
15212    init_test(cx, |_| {});
15213
15214    let mut cx = EditorTestContext::new(cx).await;
15215
15216    let buffer_id = cx.update_editor(|editor, window, cx| {
15217        editor.set_text(text, window, cx);
15218        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15219
15220        buffer_ids[0]
15221    });
15222
15223    (buffer_id, cx)
15224}
15225
15226fn assert_indent_guides(
15227    range: Range<u32>,
15228    expected: Vec<IndentGuide>,
15229    active_indices: Option<Vec<usize>>,
15230    cx: &mut EditorTestContext,
15231) {
15232    let indent_guides = cx.update_editor(|editor, window, cx| {
15233        let snapshot = editor.snapshot(window, cx).display_snapshot;
15234        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15235            editor,
15236            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15237            true,
15238            &snapshot,
15239            cx,
15240        );
15241
15242        indent_guides.sort_by(|a, b| {
15243            a.depth.cmp(&b.depth).then(
15244                a.start_row
15245                    .cmp(&b.start_row)
15246                    .then(a.end_row.cmp(&b.end_row)),
15247            )
15248        });
15249        indent_guides
15250    });
15251
15252    if let Some(expected) = active_indices {
15253        let active_indices = cx.update_editor(|editor, window, cx| {
15254            let snapshot = editor.snapshot(window, cx).display_snapshot;
15255            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15256        });
15257
15258        assert_eq!(
15259            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15260            expected,
15261            "Active indent guide indices do not match"
15262        );
15263    }
15264
15265    assert_eq!(indent_guides, expected, "Indent guides do not match");
15266}
15267
15268fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15269    IndentGuide {
15270        buffer_id,
15271        start_row: MultiBufferRow(start_row),
15272        end_row: MultiBufferRow(end_row),
15273        depth,
15274        tab_size: 4,
15275        settings: IndentGuideSettings {
15276            enabled: true,
15277            line_width: 1,
15278            active_line_width: 1,
15279            ..Default::default()
15280        },
15281    }
15282}
15283
15284#[gpui::test]
15285async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15286    let (buffer_id, mut cx) = setup_indent_guides_editor(
15287        &"
15288    fn main() {
15289        let a = 1;
15290    }"
15291        .unindent(),
15292        cx,
15293    )
15294    .await;
15295
15296    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15297}
15298
15299#[gpui::test]
15300async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15301    let (buffer_id, mut cx) = setup_indent_guides_editor(
15302        &"
15303    fn main() {
15304        let a = 1;
15305        let b = 2;
15306    }"
15307        .unindent(),
15308        cx,
15309    )
15310    .await;
15311
15312    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15313}
15314
15315#[gpui::test]
15316async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15317    let (buffer_id, mut cx) = setup_indent_guides_editor(
15318        &"
15319    fn main() {
15320        let a = 1;
15321        if a == 3 {
15322            let b = 2;
15323        } else {
15324            let c = 3;
15325        }
15326    }"
15327        .unindent(),
15328        cx,
15329    )
15330    .await;
15331
15332    assert_indent_guides(
15333        0..8,
15334        vec![
15335            indent_guide(buffer_id, 1, 6, 0),
15336            indent_guide(buffer_id, 3, 3, 1),
15337            indent_guide(buffer_id, 5, 5, 1),
15338        ],
15339        None,
15340        &mut cx,
15341    );
15342}
15343
15344#[gpui::test]
15345async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15346    let (buffer_id, mut cx) = setup_indent_guides_editor(
15347        &"
15348    fn main() {
15349        let a = 1;
15350            let b = 2;
15351        let c = 3;
15352    }"
15353        .unindent(),
15354        cx,
15355    )
15356    .await;
15357
15358    assert_indent_guides(
15359        0..5,
15360        vec![
15361            indent_guide(buffer_id, 1, 3, 0),
15362            indent_guide(buffer_id, 2, 2, 1),
15363        ],
15364        None,
15365        &mut cx,
15366    );
15367}
15368
15369#[gpui::test]
15370async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15371    let (buffer_id, mut cx) = setup_indent_guides_editor(
15372        &"
15373        fn main() {
15374            let a = 1;
15375
15376            let c = 3;
15377        }"
15378        .unindent(),
15379        cx,
15380    )
15381    .await;
15382
15383    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15384}
15385
15386#[gpui::test]
15387async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15388    let (buffer_id, mut cx) = setup_indent_guides_editor(
15389        &"
15390        fn main() {
15391            let a = 1;
15392
15393            let c = 3;
15394
15395            if a == 3 {
15396                let b = 2;
15397            } else {
15398                let c = 3;
15399            }
15400        }"
15401        .unindent(),
15402        cx,
15403    )
15404    .await;
15405
15406    assert_indent_guides(
15407        0..11,
15408        vec![
15409            indent_guide(buffer_id, 1, 9, 0),
15410            indent_guide(buffer_id, 6, 6, 1),
15411            indent_guide(buffer_id, 8, 8, 1),
15412        ],
15413        None,
15414        &mut cx,
15415    );
15416}
15417
15418#[gpui::test]
15419async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15420    let (buffer_id, mut cx) = setup_indent_guides_editor(
15421        &"
15422        fn main() {
15423            let a = 1;
15424
15425            let c = 3;
15426
15427            if a == 3 {
15428                let b = 2;
15429            } else {
15430                let c = 3;
15431            }
15432        }"
15433        .unindent(),
15434        cx,
15435    )
15436    .await;
15437
15438    assert_indent_guides(
15439        1..11,
15440        vec![
15441            indent_guide(buffer_id, 1, 9, 0),
15442            indent_guide(buffer_id, 6, 6, 1),
15443            indent_guide(buffer_id, 8, 8, 1),
15444        ],
15445        None,
15446        &mut cx,
15447    );
15448}
15449
15450#[gpui::test]
15451async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15452    let (buffer_id, mut cx) = setup_indent_guides_editor(
15453        &"
15454        fn main() {
15455            let a = 1;
15456
15457            let c = 3;
15458
15459            if a == 3 {
15460                let b = 2;
15461            } else {
15462                let c = 3;
15463            }
15464        }"
15465        .unindent(),
15466        cx,
15467    )
15468    .await;
15469
15470    assert_indent_guides(
15471        1..10,
15472        vec![
15473            indent_guide(buffer_id, 1, 9, 0),
15474            indent_guide(buffer_id, 6, 6, 1),
15475            indent_guide(buffer_id, 8, 8, 1),
15476        ],
15477        None,
15478        &mut cx,
15479    );
15480}
15481
15482#[gpui::test]
15483async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15484    let (buffer_id, mut cx) = setup_indent_guides_editor(
15485        &"
15486        block1
15487            block2
15488                block3
15489                    block4
15490            block2
15491        block1
15492        block1"
15493            .unindent(),
15494        cx,
15495    )
15496    .await;
15497
15498    assert_indent_guides(
15499        1..10,
15500        vec![
15501            indent_guide(buffer_id, 1, 4, 0),
15502            indent_guide(buffer_id, 2, 3, 1),
15503            indent_guide(buffer_id, 3, 3, 2),
15504        ],
15505        None,
15506        &mut cx,
15507    );
15508}
15509
15510#[gpui::test]
15511async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15512    let (buffer_id, mut cx) = setup_indent_guides_editor(
15513        &"
15514        block1
15515            block2
15516                block3
15517
15518        block1
15519        block1"
15520            .unindent(),
15521        cx,
15522    )
15523    .await;
15524
15525    assert_indent_guides(
15526        0..6,
15527        vec![
15528            indent_guide(buffer_id, 1, 2, 0),
15529            indent_guide(buffer_id, 2, 2, 1),
15530        ],
15531        None,
15532        &mut cx,
15533    );
15534}
15535
15536#[gpui::test]
15537async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15538    let (buffer_id, mut cx) = setup_indent_guides_editor(
15539        &"
15540        block1
15541
15542
15543
15544            block2
15545        "
15546        .unindent(),
15547        cx,
15548    )
15549    .await;
15550
15551    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15552}
15553
15554#[gpui::test]
15555async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15556    let (buffer_id, mut cx) = setup_indent_guides_editor(
15557        &"
15558        def a:
15559        \tb = 3
15560        \tif True:
15561        \t\tc = 4
15562        \t\td = 5
15563        \tprint(b)
15564        "
15565        .unindent(),
15566        cx,
15567    )
15568    .await;
15569
15570    assert_indent_guides(
15571        0..6,
15572        vec![
15573            indent_guide(buffer_id, 1, 6, 0),
15574            indent_guide(buffer_id, 3, 4, 1),
15575        ],
15576        None,
15577        &mut cx,
15578    );
15579}
15580
15581#[gpui::test]
15582async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15583    let (buffer_id, mut cx) = setup_indent_guides_editor(
15584        &"
15585    fn main() {
15586        let a = 1;
15587    }"
15588        .unindent(),
15589        cx,
15590    )
15591    .await;
15592
15593    cx.update_editor(|editor, window, cx| {
15594        editor.change_selections(None, window, cx, |s| {
15595            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15596        });
15597    });
15598
15599    assert_indent_guides(
15600        0..3,
15601        vec![indent_guide(buffer_id, 1, 1, 0)],
15602        Some(vec![0]),
15603        &mut cx,
15604    );
15605}
15606
15607#[gpui::test]
15608async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15609    let (buffer_id, mut cx) = setup_indent_guides_editor(
15610        &"
15611    fn main() {
15612        if 1 == 2 {
15613            let a = 1;
15614        }
15615    }"
15616        .unindent(),
15617        cx,
15618    )
15619    .await;
15620
15621    cx.update_editor(|editor, window, cx| {
15622        editor.change_selections(None, window, cx, |s| {
15623            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15624        });
15625    });
15626
15627    assert_indent_guides(
15628        0..4,
15629        vec![
15630            indent_guide(buffer_id, 1, 3, 0),
15631            indent_guide(buffer_id, 2, 2, 1),
15632        ],
15633        Some(vec![1]),
15634        &mut cx,
15635    );
15636
15637    cx.update_editor(|editor, window, cx| {
15638        editor.change_selections(None, window, cx, |s| {
15639            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15640        });
15641    });
15642
15643    assert_indent_guides(
15644        0..4,
15645        vec![
15646            indent_guide(buffer_id, 1, 3, 0),
15647            indent_guide(buffer_id, 2, 2, 1),
15648        ],
15649        Some(vec![1]),
15650        &mut cx,
15651    );
15652
15653    cx.update_editor(|editor, window, cx| {
15654        editor.change_selections(None, window, cx, |s| {
15655            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15656        });
15657    });
15658
15659    assert_indent_guides(
15660        0..4,
15661        vec![
15662            indent_guide(buffer_id, 1, 3, 0),
15663            indent_guide(buffer_id, 2, 2, 1),
15664        ],
15665        Some(vec![0]),
15666        &mut cx,
15667    );
15668}
15669
15670#[gpui::test]
15671async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15672    let (buffer_id, mut cx) = setup_indent_guides_editor(
15673        &"
15674    fn main() {
15675        let a = 1;
15676
15677        let b = 2;
15678    }"
15679        .unindent(),
15680        cx,
15681    )
15682    .await;
15683
15684    cx.update_editor(|editor, window, cx| {
15685        editor.change_selections(None, window, cx, |s| {
15686            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15687        });
15688    });
15689
15690    assert_indent_guides(
15691        0..5,
15692        vec![indent_guide(buffer_id, 1, 3, 0)],
15693        Some(vec![0]),
15694        &mut cx,
15695    );
15696}
15697
15698#[gpui::test]
15699async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15700    let (buffer_id, mut cx) = setup_indent_guides_editor(
15701        &"
15702    def m:
15703        a = 1
15704        pass"
15705            .unindent(),
15706        cx,
15707    )
15708    .await;
15709
15710    cx.update_editor(|editor, window, cx| {
15711        editor.change_selections(None, window, cx, |s| {
15712            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15713        });
15714    });
15715
15716    assert_indent_guides(
15717        0..3,
15718        vec![indent_guide(buffer_id, 1, 2, 0)],
15719        Some(vec![0]),
15720        &mut cx,
15721    );
15722}
15723
15724#[gpui::test]
15725async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15726    init_test(cx, |_| {});
15727    let mut cx = EditorTestContext::new(cx).await;
15728    let text = indoc! {
15729        "
15730        impl A {
15731            fn b() {
15732                0;
15733                3;
15734                5;
15735                6;
15736                7;
15737            }
15738        }
15739        "
15740    };
15741    let base_text = indoc! {
15742        "
15743        impl A {
15744            fn b() {
15745                0;
15746                1;
15747                2;
15748                3;
15749                4;
15750            }
15751            fn c() {
15752                5;
15753                6;
15754                7;
15755            }
15756        }
15757        "
15758    };
15759
15760    cx.update_editor(|editor, window, cx| {
15761        editor.set_text(text, window, cx);
15762
15763        editor.buffer().update(cx, |multibuffer, cx| {
15764            let buffer = multibuffer.as_singleton().unwrap();
15765            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15766
15767            multibuffer.set_all_diff_hunks_expanded(cx);
15768            multibuffer.add_diff(diff, cx);
15769
15770            buffer.read(cx).remote_id()
15771        })
15772    });
15773    cx.run_until_parked();
15774
15775    cx.assert_state_with_diff(
15776        indoc! { "
15777          impl A {
15778              fn b() {
15779                  0;
15780        -         1;
15781        -         2;
15782                  3;
15783        -         4;
15784        -     }
15785        -     fn c() {
15786                  5;
15787                  6;
15788                  7;
15789              }
15790          }
15791          ˇ"
15792        }
15793        .to_string(),
15794    );
15795
15796    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15797        editor
15798            .snapshot(window, cx)
15799            .buffer_snapshot
15800            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15801            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15802            .collect::<Vec<_>>()
15803    });
15804    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15805    assert_eq!(
15806        actual_guides,
15807        vec![
15808            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15809            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15810            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15811        ]
15812    );
15813}
15814
15815#[gpui::test]
15816async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15817    init_test(cx, |_| {});
15818    let mut cx = EditorTestContext::new(cx).await;
15819
15820    let diff_base = r#"
15821        a
15822        b
15823        c
15824        "#
15825    .unindent();
15826
15827    cx.set_state(
15828        &r#"
15829        ˇA
15830        b
15831        C
15832        "#
15833        .unindent(),
15834    );
15835    cx.set_head_text(&diff_base);
15836    cx.update_editor(|editor, window, cx| {
15837        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15838    });
15839    executor.run_until_parked();
15840
15841    let both_hunks_expanded = r#"
15842        - a
15843        + ˇA
15844          b
15845        - c
15846        + C
15847        "#
15848    .unindent();
15849
15850    cx.assert_state_with_diff(both_hunks_expanded.clone());
15851
15852    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15853        let snapshot = editor.snapshot(window, cx);
15854        let hunks = editor
15855            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15856            .collect::<Vec<_>>();
15857        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15858        let buffer_id = hunks[0].buffer_id;
15859        hunks
15860            .into_iter()
15861            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15862            .collect::<Vec<_>>()
15863    });
15864    assert_eq!(hunk_ranges.len(), 2);
15865
15866    cx.update_editor(|editor, _, cx| {
15867        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15868    });
15869    executor.run_until_parked();
15870
15871    let second_hunk_expanded = r#"
15872          ˇA
15873          b
15874        - c
15875        + C
15876        "#
15877    .unindent();
15878
15879    cx.assert_state_with_diff(second_hunk_expanded);
15880
15881    cx.update_editor(|editor, _, cx| {
15882        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15883    });
15884    executor.run_until_parked();
15885
15886    cx.assert_state_with_diff(both_hunks_expanded.clone());
15887
15888    cx.update_editor(|editor, _, cx| {
15889        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15890    });
15891    executor.run_until_parked();
15892
15893    let first_hunk_expanded = r#"
15894        - a
15895        + ˇA
15896          b
15897          C
15898        "#
15899    .unindent();
15900
15901    cx.assert_state_with_diff(first_hunk_expanded);
15902
15903    cx.update_editor(|editor, _, cx| {
15904        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15905    });
15906    executor.run_until_parked();
15907
15908    cx.assert_state_with_diff(both_hunks_expanded);
15909
15910    cx.set_state(
15911        &r#"
15912        ˇA
15913        b
15914        "#
15915        .unindent(),
15916    );
15917    cx.run_until_parked();
15918
15919    // TODO this cursor position seems bad
15920    cx.assert_state_with_diff(
15921        r#"
15922        - ˇa
15923        + A
15924          b
15925        "#
15926        .unindent(),
15927    );
15928
15929    cx.update_editor(|editor, window, cx| {
15930        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15931    });
15932
15933    cx.assert_state_with_diff(
15934        r#"
15935            - ˇa
15936            + A
15937              b
15938            - c
15939            "#
15940        .unindent(),
15941    );
15942
15943    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15944        let snapshot = editor.snapshot(window, cx);
15945        let hunks = editor
15946            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15947            .collect::<Vec<_>>();
15948        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15949        let buffer_id = hunks[0].buffer_id;
15950        hunks
15951            .into_iter()
15952            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15953            .collect::<Vec<_>>()
15954    });
15955    assert_eq!(hunk_ranges.len(), 2);
15956
15957    cx.update_editor(|editor, _, cx| {
15958        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15959    });
15960    executor.run_until_parked();
15961
15962    cx.assert_state_with_diff(
15963        r#"
15964        - ˇa
15965        + A
15966          b
15967        "#
15968        .unindent(),
15969    );
15970}
15971
15972#[gpui::test]
15973async fn test_toggle_deletion_hunk_at_start_of_file(
15974    executor: BackgroundExecutor,
15975    cx: &mut TestAppContext,
15976) {
15977    init_test(cx, |_| {});
15978    let mut cx = EditorTestContext::new(cx).await;
15979
15980    let diff_base = r#"
15981        a
15982        b
15983        c
15984        "#
15985    .unindent();
15986
15987    cx.set_state(
15988        &r#"
15989        ˇb
15990        c
15991        "#
15992        .unindent(),
15993    );
15994    cx.set_head_text(&diff_base);
15995    cx.update_editor(|editor, window, cx| {
15996        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15997    });
15998    executor.run_until_parked();
15999
16000    let hunk_expanded = r#"
16001        - a
16002          ˇb
16003          c
16004        "#
16005    .unindent();
16006
16007    cx.assert_state_with_diff(hunk_expanded.clone());
16008
16009    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16010        let snapshot = editor.snapshot(window, cx);
16011        let hunks = editor
16012            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16013            .collect::<Vec<_>>();
16014        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16015        let buffer_id = hunks[0].buffer_id;
16016        hunks
16017            .into_iter()
16018            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16019            .collect::<Vec<_>>()
16020    });
16021    assert_eq!(hunk_ranges.len(), 1);
16022
16023    cx.update_editor(|editor, _, cx| {
16024        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16025    });
16026    executor.run_until_parked();
16027
16028    let hunk_collapsed = r#"
16029          ˇb
16030          c
16031        "#
16032    .unindent();
16033
16034    cx.assert_state_with_diff(hunk_collapsed);
16035
16036    cx.update_editor(|editor, _, cx| {
16037        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16038    });
16039    executor.run_until_parked();
16040
16041    cx.assert_state_with_diff(hunk_expanded.clone());
16042}
16043
16044#[gpui::test]
16045async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16046    init_test(cx, |_| {});
16047
16048    let fs = FakeFs::new(cx.executor());
16049    fs.insert_tree(
16050        path!("/test"),
16051        json!({
16052            ".git": {},
16053            "file-1": "ONE\n",
16054            "file-2": "TWO\n",
16055            "file-3": "THREE\n",
16056        }),
16057    )
16058    .await;
16059
16060    fs.set_head_for_repo(
16061        path!("/test/.git").as_ref(),
16062        &[
16063            ("file-1".into(), "one\n".into()),
16064            ("file-2".into(), "two\n".into()),
16065            ("file-3".into(), "three\n".into()),
16066        ],
16067    );
16068
16069    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16070    let mut buffers = vec![];
16071    for i in 1..=3 {
16072        let buffer = project
16073            .update(cx, |project, cx| {
16074                let path = format!(path!("/test/file-{}"), i);
16075                project.open_local_buffer(path, cx)
16076            })
16077            .await
16078            .unwrap();
16079        buffers.push(buffer);
16080    }
16081
16082    let multibuffer = cx.new(|cx| {
16083        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16084        multibuffer.set_all_diff_hunks_expanded(cx);
16085        for buffer in &buffers {
16086            let snapshot = buffer.read(cx).snapshot();
16087            multibuffer.set_excerpts_for_path(
16088                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16089                buffer.clone(),
16090                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16091                DEFAULT_MULTIBUFFER_CONTEXT,
16092                cx,
16093            );
16094        }
16095        multibuffer
16096    });
16097
16098    let editor = cx.add_window(|window, cx| {
16099        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16100    });
16101    cx.run_until_parked();
16102
16103    let snapshot = editor
16104        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16105        .unwrap();
16106    let hunks = snapshot
16107        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16108        .map(|hunk| match hunk {
16109            DisplayDiffHunk::Unfolded {
16110                display_row_range, ..
16111            } => display_row_range,
16112            DisplayDiffHunk::Folded { .. } => unreachable!(),
16113        })
16114        .collect::<Vec<_>>();
16115    assert_eq!(
16116        hunks,
16117        [
16118            DisplayRow(2)..DisplayRow(4),
16119            DisplayRow(7)..DisplayRow(9),
16120            DisplayRow(12)..DisplayRow(14),
16121        ]
16122    );
16123}
16124
16125#[gpui::test]
16126async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16127    init_test(cx, |_| {});
16128
16129    let mut cx = EditorTestContext::new(cx).await;
16130    cx.set_head_text(indoc! { "
16131        one
16132        two
16133        three
16134        four
16135        five
16136        "
16137    });
16138    cx.set_index_text(indoc! { "
16139        one
16140        two
16141        three
16142        four
16143        five
16144        "
16145    });
16146    cx.set_state(indoc! {"
16147        one
16148        TWO
16149        ˇTHREE
16150        FOUR
16151        five
16152    "});
16153    cx.run_until_parked();
16154    cx.update_editor(|editor, window, cx| {
16155        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16156    });
16157    cx.run_until_parked();
16158    cx.assert_index_text(Some(indoc! {"
16159        one
16160        TWO
16161        THREE
16162        FOUR
16163        five
16164    "}));
16165    cx.set_state(indoc! { "
16166        one
16167        TWO
16168        ˇTHREE-HUNDRED
16169        FOUR
16170        five
16171    "});
16172    cx.run_until_parked();
16173    cx.update_editor(|editor, window, cx| {
16174        let snapshot = editor.snapshot(window, cx);
16175        let hunks = editor
16176            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16177            .collect::<Vec<_>>();
16178        assert_eq!(hunks.len(), 1);
16179        assert_eq!(
16180            hunks[0].status(),
16181            DiffHunkStatus {
16182                kind: DiffHunkStatusKind::Modified,
16183                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16184            }
16185        );
16186
16187        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16188    });
16189    cx.run_until_parked();
16190    cx.assert_index_text(Some(indoc! {"
16191        one
16192        TWO
16193        THREE-HUNDRED
16194        FOUR
16195        five
16196    "}));
16197}
16198
16199#[gpui::test]
16200fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16201    init_test(cx, |_| {});
16202
16203    let editor = cx.add_window(|window, cx| {
16204        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16205        build_editor(buffer, window, cx)
16206    });
16207
16208    let render_args = Arc::new(Mutex::new(None));
16209    let snapshot = editor
16210        .update(cx, |editor, window, cx| {
16211            let snapshot = editor.buffer().read(cx).snapshot(cx);
16212            let range =
16213                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16214
16215            struct RenderArgs {
16216                row: MultiBufferRow,
16217                folded: bool,
16218                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16219            }
16220
16221            let crease = Crease::inline(
16222                range,
16223                FoldPlaceholder::test(),
16224                {
16225                    let toggle_callback = render_args.clone();
16226                    move |row, folded, callback, _window, _cx| {
16227                        *toggle_callback.lock() = Some(RenderArgs {
16228                            row,
16229                            folded,
16230                            callback,
16231                        });
16232                        div()
16233                    }
16234                },
16235                |_row, _folded, _window, _cx| div(),
16236            );
16237
16238            editor.insert_creases(Some(crease), cx);
16239            let snapshot = editor.snapshot(window, cx);
16240            let _div = snapshot.render_crease_toggle(
16241                MultiBufferRow(1),
16242                false,
16243                cx.entity().clone(),
16244                window,
16245                cx,
16246            );
16247            snapshot
16248        })
16249        .unwrap();
16250
16251    let render_args = render_args.lock().take().unwrap();
16252    assert_eq!(render_args.row, MultiBufferRow(1));
16253    assert!(!render_args.folded);
16254    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16255
16256    cx.update_window(*editor, |_, window, cx| {
16257        (render_args.callback)(true, window, cx)
16258    })
16259    .unwrap();
16260    let snapshot = editor
16261        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16262        .unwrap();
16263    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16264
16265    cx.update_window(*editor, |_, window, cx| {
16266        (render_args.callback)(false, window, cx)
16267    })
16268    .unwrap();
16269    let snapshot = editor
16270        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16271        .unwrap();
16272    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16273}
16274
16275#[gpui::test]
16276async fn test_input_text(cx: &mut TestAppContext) {
16277    init_test(cx, |_| {});
16278    let mut cx = EditorTestContext::new(cx).await;
16279
16280    cx.set_state(
16281        &r#"ˇone
16282        two
16283
16284        three
16285        fourˇ
16286        five
16287
16288        siˇx"#
16289            .unindent(),
16290    );
16291
16292    cx.dispatch_action(HandleInput(String::new()));
16293    cx.assert_editor_state(
16294        &r#"ˇone
16295        two
16296
16297        three
16298        fourˇ
16299        five
16300
16301        siˇx"#
16302            .unindent(),
16303    );
16304
16305    cx.dispatch_action(HandleInput("AAAA".to_string()));
16306    cx.assert_editor_state(
16307        &r#"AAAAˇone
16308        two
16309
16310        three
16311        fourAAAAˇ
16312        five
16313
16314        siAAAAˇx"#
16315            .unindent(),
16316    );
16317}
16318
16319#[gpui::test]
16320async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16321    init_test(cx, |_| {});
16322
16323    let mut cx = EditorTestContext::new(cx).await;
16324    cx.set_state(
16325        r#"let foo = 1;
16326let foo = 2;
16327let foo = 3;
16328let fooˇ = 4;
16329let foo = 5;
16330let foo = 6;
16331let foo = 7;
16332let foo = 8;
16333let foo = 9;
16334let foo = 10;
16335let foo = 11;
16336let foo = 12;
16337let foo = 13;
16338let foo = 14;
16339let foo = 15;"#,
16340    );
16341
16342    cx.update_editor(|e, window, cx| {
16343        assert_eq!(
16344            e.next_scroll_position,
16345            NextScrollCursorCenterTopBottom::Center,
16346            "Default next scroll direction is center",
16347        );
16348
16349        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16350        assert_eq!(
16351            e.next_scroll_position,
16352            NextScrollCursorCenterTopBottom::Top,
16353            "After center, next scroll direction should be top",
16354        );
16355
16356        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16357        assert_eq!(
16358            e.next_scroll_position,
16359            NextScrollCursorCenterTopBottom::Bottom,
16360            "After top, next scroll direction should be bottom",
16361        );
16362
16363        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16364        assert_eq!(
16365            e.next_scroll_position,
16366            NextScrollCursorCenterTopBottom::Center,
16367            "After bottom, scrolling should start over",
16368        );
16369
16370        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16371        assert_eq!(
16372            e.next_scroll_position,
16373            NextScrollCursorCenterTopBottom::Top,
16374            "Scrolling continues if retriggered fast enough"
16375        );
16376    });
16377
16378    cx.executor()
16379        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16380    cx.executor().run_until_parked();
16381    cx.update_editor(|e, _, _| {
16382        assert_eq!(
16383            e.next_scroll_position,
16384            NextScrollCursorCenterTopBottom::Center,
16385            "If scrolling is not triggered fast enough, it should reset"
16386        );
16387    });
16388}
16389
16390#[gpui::test]
16391async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16392    init_test(cx, |_| {});
16393    let mut cx = EditorLspTestContext::new_rust(
16394        lsp::ServerCapabilities {
16395            definition_provider: Some(lsp::OneOf::Left(true)),
16396            references_provider: Some(lsp::OneOf::Left(true)),
16397            ..lsp::ServerCapabilities::default()
16398        },
16399        cx,
16400    )
16401    .await;
16402
16403    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16404        let go_to_definition = cx
16405            .lsp
16406            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16407                move |params, _| async move {
16408                    if empty_go_to_definition {
16409                        Ok(None)
16410                    } else {
16411                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16412                            uri: params.text_document_position_params.text_document.uri,
16413                            range: lsp::Range::new(
16414                                lsp::Position::new(4, 3),
16415                                lsp::Position::new(4, 6),
16416                            ),
16417                        })))
16418                    }
16419                },
16420            );
16421        let references = cx
16422            .lsp
16423            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16424                Ok(Some(vec![lsp::Location {
16425                    uri: params.text_document_position.text_document.uri,
16426                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16427                }]))
16428            });
16429        (go_to_definition, references)
16430    };
16431
16432    cx.set_state(
16433        &r#"fn one() {
16434            let mut a = ˇtwo();
16435        }
16436
16437        fn two() {}"#
16438            .unindent(),
16439    );
16440    set_up_lsp_handlers(false, &mut cx);
16441    let navigated = cx
16442        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16443        .await
16444        .expect("Failed to navigate to definition");
16445    assert_eq!(
16446        navigated,
16447        Navigated::Yes,
16448        "Should have navigated to definition from the GetDefinition response"
16449    );
16450    cx.assert_editor_state(
16451        &r#"fn one() {
16452            let mut a = two();
16453        }
16454
16455        fn «twoˇ»() {}"#
16456            .unindent(),
16457    );
16458
16459    let editors = cx.update_workspace(|workspace, _, cx| {
16460        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16461    });
16462    cx.update_editor(|_, _, test_editor_cx| {
16463        assert_eq!(
16464            editors.len(),
16465            1,
16466            "Initially, only one, test, editor should be open in the workspace"
16467        );
16468        assert_eq!(
16469            test_editor_cx.entity(),
16470            editors.last().expect("Asserted len is 1").clone()
16471        );
16472    });
16473
16474    set_up_lsp_handlers(true, &mut cx);
16475    let navigated = cx
16476        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16477        .await
16478        .expect("Failed to navigate to lookup references");
16479    assert_eq!(
16480        navigated,
16481        Navigated::Yes,
16482        "Should have navigated to references as a fallback after empty GoToDefinition response"
16483    );
16484    // We should not change the selections in the existing file,
16485    // if opening another milti buffer with the references
16486    cx.assert_editor_state(
16487        &r#"fn one() {
16488            let mut a = two();
16489        }
16490
16491        fn «twoˇ»() {}"#
16492            .unindent(),
16493    );
16494    let editors = cx.update_workspace(|workspace, _, cx| {
16495        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16496    });
16497    cx.update_editor(|_, _, test_editor_cx| {
16498        assert_eq!(
16499            editors.len(),
16500            2,
16501            "After falling back to references search, we open a new editor with the results"
16502        );
16503        let references_fallback_text = editors
16504            .into_iter()
16505            .find(|new_editor| *new_editor != test_editor_cx.entity())
16506            .expect("Should have one non-test editor now")
16507            .read(test_editor_cx)
16508            .text(test_editor_cx);
16509        assert_eq!(
16510            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16511            "Should use the range from the references response and not the GoToDefinition one"
16512        );
16513    });
16514}
16515
16516#[gpui::test]
16517async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16518    init_test(cx, |_| {});
16519    cx.update(|cx| {
16520        let mut editor_settings = EditorSettings::get_global(cx).clone();
16521        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16522        EditorSettings::override_global(editor_settings, cx);
16523    });
16524    let mut cx = EditorLspTestContext::new_rust(
16525        lsp::ServerCapabilities {
16526            definition_provider: Some(lsp::OneOf::Left(true)),
16527            references_provider: Some(lsp::OneOf::Left(true)),
16528            ..lsp::ServerCapabilities::default()
16529        },
16530        cx,
16531    )
16532    .await;
16533    let original_state = r#"fn one() {
16534        let mut a = ˇtwo();
16535    }
16536
16537    fn two() {}"#
16538        .unindent();
16539    cx.set_state(&original_state);
16540
16541    let mut go_to_definition = cx
16542        .lsp
16543        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16544            move |_, _| async move { Ok(None) },
16545        );
16546    let _references = cx
16547        .lsp
16548        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16549            panic!("Should not call for references with no go to definition fallback")
16550        });
16551
16552    let navigated = cx
16553        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16554        .await
16555        .expect("Failed to navigate to lookup references");
16556    go_to_definition
16557        .next()
16558        .await
16559        .expect("Should have called the go_to_definition handler");
16560
16561    assert_eq!(
16562        navigated,
16563        Navigated::No,
16564        "Should have navigated to references as a fallback after empty GoToDefinition response"
16565    );
16566    cx.assert_editor_state(&original_state);
16567    let editors = cx.update_workspace(|workspace, _, cx| {
16568        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16569    });
16570    cx.update_editor(|_, _, _| {
16571        assert_eq!(
16572            editors.len(),
16573            1,
16574            "After unsuccessful fallback, no other editor should have been opened"
16575        );
16576    });
16577}
16578
16579#[gpui::test]
16580async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16581    init_test(cx, |_| {});
16582
16583    let language = Arc::new(Language::new(
16584        LanguageConfig::default(),
16585        Some(tree_sitter_rust::LANGUAGE.into()),
16586    ));
16587
16588    let text = r#"
16589        #[cfg(test)]
16590        mod tests() {
16591            #[test]
16592            fn runnable_1() {
16593                let a = 1;
16594            }
16595
16596            #[test]
16597            fn runnable_2() {
16598                let a = 1;
16599                let b = 2;
16600            }
16601        }
16602    "#
16603    .unindent();
16604
16605    let fs = FakeFs::new(cx.executor());
16606    fs.insert_file("/file.rs", Default::default()).await;
16607
16608    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16609    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16610    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16611    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16612    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16613
16614    let editor = cx.new_window_entity(|window, cx| {
16615        Editor::new(
16616            EditorMode::Full,
16617            multi_buffer,
16618            Some(project.clone()),
16619            window,
16620            cx,
16621        )
16622    });
16623
16624    editor.update_in(cx, |editor, window, cx| {
16625        let snapshot = editor.buffer().read(cx).snapshot(cx);
16626        editor.tasks.insert(
16627            (buffer.read(cx).remote_id(), 3),
16628            RunnableTasks {
16629                templates: vec![],
16630                offset: snapshot.anchor_before(43),
16631                column: 0,
16632                extra_variables: HashMap::default(),
16633                context_range: BufferOffset(43)..BufferOffset(85),
16634            },
16635        );
16636        editor.tasks.insert(
16637            (buffer.read(cx).remote_id(), 8),
16638            RunnableTasks {
16639                templates: vec![],
16640                offset: snapshot.anchor_before(86),
16641                column: 0,
16642                extra_variables: HashMap::default(),
16643                context_range: BufferOffset(86)..BufferOffset(191),
16644            },
16645        );
16646
16647        // Test finding task when cursor is inside function body
16648        editor.change_selections(None, window, cx, |s| {
16649            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16650        });
16651        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16652        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16653
16654        // Test finding task when cursor is on function name
16655        editor.change_selections(None, window, cx, |s| {
16656            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16657        });
16658        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16659        assert_eq!(row, 8, "Should find task when cursor is on function name");
16660    });
16661}
16662
16663#[gpui::test]
16664async fn test_folding_buffers(cx: &mut TestAppContext) {
16665    init_test(cx, |_| {});
16666
16667    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16668    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16669    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16670
16671    let fs = FakeFs::new(cx.executor());
16672    fs.insert_tree(
16673        path!("/a"),
16674        json!({
16675            "first.rs": sample_text_1,
16676            "second.rs": sample_text_2,
16677            "third.rs": sample_text_3,
16678        }),
16679    )
16680    .await;
16681    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16682    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16683    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16684    let worktree = project.update(cx, |project, cx| {
16685        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16686        assert_eq!(worktrees.len(), 1);
16687        worktrees.pop().unwrap()
16688    });
16689    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16690
16691    let buffer_1 = project
16692        .update(cx, |project, cx| {
16693            project.open_buffer((worktree_id, "first.rs"), cx)
16694        })
16695        .await
16696        .unwrap();
16697    let buffer_2 = project
16698        .update(cx, |project, cx| {
16699            project.open_buffer((worktree_id, "second.rs"), cx)
16700        })
16701        .await
16702        .unwrap();
16703    let buffer_3 = project
16704        .update(cx, |project, cx| {
16705            project.open_buffer((worktree_id, "third.rs"), cx)
16706        })
16707        .await
16708        .unwrap();
16709
16710    let multi_buffer = cx.new(|cx| {
16711        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16712        multi_buffer.push_excerpts(
16713            buffer_1.clone(),
16714            [
16715                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16716                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16717                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16718            ],
16719            cx,
16720        );
16721        multi_buffer.push_excerpts(
16722            buffer_2.clone(),
16723            [
16724                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16725                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16726                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16727            ],
16728            cx,
16729        );
16730        multi_buffer.push_excerpts(
16731            buffer_3.clone(),
16732            [
16733                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16734                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16735                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16736            ],
16737            cx,
16738        );
16739        multi_buffer
16740    });
16741    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16742        Editor::new(
16743            EditorMode::Full,
16744            multi_buffer.clone(),
16745            Some(project.clone()),
16746            window,
16747            cx,
16748        )
16749    });
16750
16751    assert_eq!(
16752        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16753        "\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",
16754    );
16755
16756    multi_buffer_editor.update(cx, |editor, cx| {
16757        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16758    });
16759    assert_eq!(
16760        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16761        "\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",
16762        "After folding the first buffer, its text should not be displayed"
16763    );
16764
16765    multi_buffer_editor.update(cx, |editor, cx| {
16766        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16767    });
16768    assert_eq!(
16769        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16770        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16771        "After folding the second buffer, its text should not be displayed"
16772    );
16773
16774    multi_buffer_editor.update(cx, |editor, cx| {
16775        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16776    });
16777    assert_eq!(
16778        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16779        "\n\n\n\n\n",
16780        "After folding the third buffer, its text should not be displayed"
16781    );
16782
16783    // Emulate selection inside the fold logic, that should work
16784    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16785        editor
16786            .snapshot(window, cx)
16787            .next_line_boundary(Point::new(0, 4));
16788    });
16789
16790    multi_buffer_editor.update(cx, |editor, cx| {
16791        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16792    });
16793    assert_eq!(
16794        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16795        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16796        "After unfolding the second buffer, its text should be displayed"
16797    );
16798
16799    // Typing inside of buffer 1 causes that buffer to be unfolded.
16800    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16801        assert_eq!(
16802            multi_buffer
16803                .read(cx)
16804                .snapshot(cx)
16805                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16806                .collect::<String>(),
16807            "bbbb"
16808        );
16809        editor.change_selections(None, window, cx, |selections| {
16810            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16811        });
16812        editor.handle_input("B", window, cx);
16813    });
16814
16815    assert_eq!(
16816        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16817        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16818        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16819    );
16820
16821    multi_buffer_editor.update(cx, |editor, cx| {
16822        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16823    });
16824    assert_eq!(
16825        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16826        "\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",
16827        "After unfolding the all buffers, all original text should be displayed"
16828    );
16829}
16830
16831#[gpui::test]
16832async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16833    init_test(cx, |_| {});
16834
16835    let sample_text_1 = "1111\n2222\n3333".to_string();
16836    let sample_text_2 = "4444\n5555\n6666".to_string();
16837    let sample_text_3 = "7777\n8888\n9999".to_string();
16838
16839    let fs = FakeFs::new(cx.executor());
16840    fs.insert_tree(
16841        path!("/a"),
16842        json!({
16843            "first.rs": sample_text_1,
16844            "second.rs": sample_text_2,
16845            "third.rs": sample_text_3,
16846        }),
16847    )
16848    .await;
16849    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16850    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16851    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16852    let worktree = project.update(cx, |project, cx| {
16853        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16854        assert_eq!(worktrees.len(), 1);
16855        worktrees.pop().unwrap()
16856    });
16857    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16858
16859    let buffer_1 = project
16860        .update(cx, |project, cx| {
16861            project.open_buffer((worktree_id, "first.rs"), cx)
16862        })
16863        .await
16864        .unwrap();
16865    let buffer_2 = project
16866        .update(cx, |project, cx| {
16867            project.open_buffer((worktree_id, "second.rs"), cx)
16868        })
16869        .await
16870        .unwrap();
16871    let buffer_3 = project
16872        .update(cx, |project, cx| {
16873            project.open_buffer((worktree_id, "third.rs"), cx)
16874        })
16875        .await
16876        .unwrap();
16877
16878    let multi_buffer = cx.new(|cx| {
16879        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16880        multi_buffer.push_excerpts(
16881            buffer_1.clone(),
16882            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16883            cx,
16884        );
16885        multi_buffer.push_excerpts(
16886            buffer_2.clone(),
16887            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16888            cx,
16889        );
16890        multi_buffer.push_excerpts(
16891            buffer_3.clone(),
16892            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16893            cx,
16894        );
16895        multi_buffer
16896    });
16897
16898    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16899        Editor::new(
16900            EditorMode::Full,
16901            multi_buffer,
16902            Some(project.clone()),
16903            window,
16904            cx,
16905        )
16906    });
16907
16908    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16909    assert_eq!(
16910        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16911        full_text,
16912    );
16913
16914    multi_buffer_editor.update(cx, |editor, cx| {
16915        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16916    });
16917    assert_eq!(
16918        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16919        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16920        "After folding the first buffer, its text should not be displayed"
16921    );
16922
16923    multi_buffer_editor.update(cx, |editor, cx| {
16924        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16925    });
16926
16927    assert_eq!(
16928        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16929        "\n\n\n\n\n\n7777\n8888\n9999",
16930        "After folding the second buffer, its text should not be displayed"
16931    );
16932
16933    multi_buffer_editor.update(cx, |editor, cx| {
16934        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16935    });
16936    assert_eq!(
16937        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16938        "\n\n\n\n\n",
16939        "After folding the third buffer, its text should not be displayed"
16940    );
16941
16942    multi_buffer_editor.update(cx, |editor, cx| {
16943        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16944    });
16945    assert_eq!(
16946        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16947        "\n\n\n\n4444\n5555\n6666\n\n",
16948        "After unfolding the second buffer, its text should be displayed"
16949    );
16950
16951    multi_buffer_editor.update(cx, |editor, cx| {
16952        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16953    });
16954    assert_eq!(
16955        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16956        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16957        "After unfolding the first buffer, its text should be displayed"
16958    );
16959
16960    multi_buffer_editor.update(cx, |editor, cx| {
16961        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16962    });
16963    assert_eq!(
16964        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16965        full_text,
16966        "After unfolding all buffers, all original text should be displayed"
16967    );
16968}
16969
16970#[gpui::test]
16971async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16972    init_test(cx, |_| {});
16973
16974    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16975
16976    let fs = FakeFs::new(cx.executor());
16977    fs.insert_tree(
16978        path!("/a"),
16979        json!({
16980            "main.rs": sample_text,
16981        }),
16982    )
16983    .await;
16984    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16985    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16986    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16987    let worktree = project.update(cx, |project, cx| {
16988        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16989        assert_eq!(worktrees.len(), 1);
16990        worktrees.pop().unwrap()
16991    });
16992    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16993
16994    let buffer_1 = project
16995        .update(cx, |project, cx| {
16996            project.open_buffer((worktree_id, "main.rs"), cx)
16997        })
16998        .await
16999        .unwrap();
17000
17001    let multi_buffer = cx.new(|cx| {
17002        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17003        multi_buffer.push_excerpts(
17004            buffer_1.clone(),
17005            [ExcerptRange::new(
17006                Point::new(0, 0)
17007                    ..Point::new(
17008                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17009                        0,
17010                    ),
17011            )],
17012            cx,
17013        );
17014        multi_buffer
17015    });
17016    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17017        Editor::new(
17018            EditorMode::Full,
17019            multi_buffer,
17020            Some(project.clone()),
17021            window,
17022            cx,
17023        )
17024    });
17025
17026    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17027    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17028        enum TestHighlight {}
17029        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17030        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17031        editor.highlight_text::<TestHighlight>(
17032            vec![highlight_range.clone()],
17033            HighlightStyle::color(Hsla::green()),
17034            cx,
17035        );
17036        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17037    });
17038
17039    let full_text = format!("\n\n{sample_text}");
17040    assert_eq!(
17041        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17042        full_text,
17043    );
17044}
17045
17046#[gpui::test]
17047async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17048    init_test(cx, |_| {});
17049    cx.update(|cx| {
17050        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17051            "keymaps/default-linux.json",
17052            cx,
17053        )
17054        .unwrap();
17055        cx.bind_keys(default_key_bindings);
17056    });
17057
17058    let (editor, cx) = cx.add_window_view(|window, cx| {
17059        let multi_buffer = MultiBuffer::build_multi(
17060            [
17061                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17062                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17063                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17064                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17065            ],
17066            cx,
17067        );
17068        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17069
17070        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17071        // fold all but the second buffer, so that we test navigating between two
17072        // adjacent folded buffers, as well as folded buffers at the start and
17073        // end the multibuffer
17074        editor.fold_buffer(buffer_ids[0], cx);
17075        editor.fold_buffer(buffer_ids[2], cx);
17076        editor.fold_buffer(buffer_ids[3], cx);
17077
17078        editor
17079    });
17080    cx.simulate_resize(size(px(1000.), px(1000.)));
17081
17082    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17083    cx.assert_excerpts_with_selections(indoc! {"
17084        [EXCERPT]
17085        ˇ[FOLDED]
17086        [EXCERPT]
17087        a1
17088        b1
17089        [EXCERPT]
17090        [FOLDED]
17091        [EXCERPT]
17092        [FOLDED]
17093        "
17094    });
17095    cx.simulate_keystroke("down");
17096    cx.assert_excerpts_with_selections(indoc! {"
17097        [EXCERPT]
17098        [FOLDED]
17099        [EXCERPT]
17100        ˇa1
17101        b1
17102        [EXCERPT]
17103        [FOLDED]
17104        [EXCERPT]
17105        [FOLDED]
17106        "
17107    });
17108    cx.simulate_keystroke("down");
17109    cx.assert_excerpts_with_selections(indoc! {"
17110        [EXCERPT]
17111        [FOLDED]
17112        [EXCERPT]
17113        a1
17114        ˇb1
17115        [EXCERPT]
17116        [FOLDED]
17117        [EXCERPT]
17118        [FOLDED]
17119        "
17120    });
17121    cx.simulate_keystroke("down");
17122    cx.assert_excerpts_with_selections(indoc! {"
17123        [EXCERPT]
17124        [FOLDED]
17125        [EXCERPT]
17126        a1
17127        b1
17128        ˇ[EXCERPT]
17129        [FOLDED]
17130        [EXCERPT]
17131        [FOLDED]
17132        "
17133    });
17134    cx.simulate_keystroke("down");
17135    cx.assert_excerpts_with_selections(indoc! {"
17136        [EXCERPT]
17137        [FOLDED]
17138        [EXCERPT]
17139        a1
17140        b1
17141        [EXCERPT]
17142        ˇ[FOLDED]
17143        [EXCERPT]
17144        [FOLDED]
17145        "
17146    });
17147    for _ in 0..5 {
17148        cx.simulate_keystroke("down");
17149        cx.assert_excerpts_with_selections(indoc! {"
17150            [EXCERPT]
17151            [FOLDED]
17152            [EXCERPT]
17153            a1
17154            b1
17155            [EXCERPT]
17156            [FOLDED]
17157            [EXCERPT]
17158            ˇ[FOLDED]
17159            "
17160        });
17161    }
17162
17163    cx.simulate_keystroke("up");
17164    cx.assert_excerpts_with_selections(indoc! {"
17165        [EXCERPT]
17166        [FOLDED]
17167        [EXCERPT]
17168        a1
17169        b1
17170        [EXCERPT]
17171        ˇ[FOLDED]
17172        [EXCERPT]
17173        [FOLDED]
17174        "
17175    });
17176    cx.simulate_keystroke("up");
17177    cx.assert_excerpts_with_selections(indoc! {"
17178        [EXCERPT]
17179        [FOLDED]
17180        [EXCERPT]
17181        a1
17182        b1
17183        ˇ[EXCERPT]
17184        [FOLDED]
17185        [EXCERPT]
17186        [FOLDED]
17187        "
17188    });
17189    cx.simulate_keystroke("up");
17190    cx.assert_excerpts_with_selections(indoc! {"
17191        [EXCERPT]
17192        [FOLDED]
17193        [EXCERPT]
17194        a1
17195        ˇb1
17196        [EXCERPT]
17197        [FOLDED]
17198        [EXCERPT]
17199        [FOLDED]
17200        "
17201    });
17202    cx.simulate_keystroke("up");
17203    cx.assert_excerpts_with_selections(indoc! {"
17204        [EXCERPT]
17205        [FOLDED]
17206        [EXCERPT]
17207        ˇa1
17208        b1
17209        [EXCERPT]
17210        [FOLDED]
17211        [EXCERPT]
17212        [FOLDED]
17213        "
17214    });
17215    for _ in 0..5 {
17216        cx.simulate_keystroke("up");
17217        cx.assert_excerpts_with_selections(indoc! {"
17218            [EXCERPT]
17219            ˇ[FOLDED]
17220            [EXCERPT]
17221            a1
17222            b1
17223            [EXCERPT]
17224            [FOLDED]
17225            [EXCERPT]
17226            [FOLDED]
17227            "
17228        });
17229    }
17230}
17231
17232#[gpui::test]
17233async fn test_inline_completion_text(cx: &mut TestAppContext) {
17234    init_test(cx, |_| {});
17235
17236    // Simple insertion
17237    assert_highlighted_edits(
17238        "Hello, world!",
17239        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17240        true,
17241        cx,
17242        |highlighted_edits, cx| {
17243            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17244            assert_eq!(highlighted_edits.highlights.len(), 1);
17245            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17246            assert_eq!(
17247                highlighted_edits.highlights[0].1.background_color,
17248                Some(cx.theme().status().created_background)
17249            );
17250        },
17251    )
17252    .await;
17253
17254    // Replacement
17255    assert_highlighted_edits(
17256        "This is a test.",
17257        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17258        false,
17259        cx,
17260        |highlighted_edits, cx| {
17261            assert_eq!(highlighted_edits.text, "That is a test.");
17262            assert_eq!(highlighted_edits.highlights.len(), 1);
17263            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17264            assert_eq!(
17265                highlighted_edits.highlights[0].1.background_color,
17266                Some(cx.theme().status().created_background)
17267            );
17268        },
17269    )
17270    .await;
17271
17272    // Multiple edits
17273    assert_highlighted_edits(
17274        "Hello, world!",
17275        vec![
17276            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17277            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17278        ],
17279        false,
17280        cx,
17281        |highlighted_edits, cx| {
17282            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17283            assert_eq!(highlighted_edits.highlights.len(), 2);
17284            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17285            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17286            assert_eq!(
17287                highlighted_edits.highlights[0].1.background_color,
17288                Some(cx.theme().status().created_background)
17289            );
17290            assert_eq!(
17291                highlighted_edits.highlights[1].1.background_color,
17292                Some(cx.theme().status().created_background)
17293            );
17294        },
17295    )
17296    .await;
17297
17298    // Multiple lines with edits
17299    assert_highlighted_edits(
17300        "First line\nSecond line\nThird line\nFourth line",
17301        vec![
17302            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17303            (
17304                Point::new(2, 0)..Point::new(2, 10),
17305                "New third line".to_string(),
17306            ),
17307            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17308        ],
17309        false,
17310        cx,
17311        |highlighted_edits, cx| {
17312            assert_eq!(
17313                highlighted_edits.text,
17314                "Second modified\nNew third line\nFourth updated line"
17315            );
17316            assert_eq!(highlighted_edits.highlights.len(), 3);
17317            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17318            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17319            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17320            for highlight in &highlighted_edits.highlights {
17321                assert_eq!(
17322                    highlight.1.background_color,
17323                    Some(cx.theme().status().created_background)
17324                );
17325            }
17326        },
17327    )
17328    .await;
17329}
17330
17331#[gpui::test]
17332async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17333    init_test(cx, |_| {});
17334
17335    // Deletion
17336    assert_highlighted_edits(
17337        "Hello, world!",
17338        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17339        true,
17340        cx,
17341        |highlighted_edits, cx| {
17342            assert_eq!(highlighted_edits.text, "Hello, world!");
17343            assert_eq!(highlighted_edits.highlights.len(), 1);
17344            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17345            assert_eq!(
17346                highlighted_edits.highlights[0].1.background_color,
17347                Some(cx.theme().status().deleted_background)
17348            );
17349        },
17350    )
17351    .await;
17352
17353    // Insertion
17354    assert_highlighted_edits(
17355        "Hello, world!",
17356        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17357        true,
17358        cx,
17359        |highlighted_edits, cx| {
17360            assert_eq!(highlighted_edits.highlights.len(), 1);
17361            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17362            assert_eq!(
17363                highlighted_edits.highlights[0].1.background_color,
17364                Some(cx.theme().status().created_background)
17365            );
17366        },
17367    )
17368    .await;
17369}
17370
17371async fn assert_highlighted_edits(
17372    text: &str,
17373    edits: Vec<(Range<Point>, String)>,
17374    include_deletions: bool,
17375    cx: &mut TestAppContext,
17376    assertion_fn: impl Fn(HighlightedText, &App),
17377) {
17378    let window = cx.add_window(|window, cx| {
17379        let buffer = MultiBuffer::build_simple(text, cx);
17380        Editor::new(EditorMode::Full, buffer, None, window, cx)
17381    });
17382    let cx = &mut VisualTestContext::from_window(*window, cx);
17383
17384    let (buffer, snapshot) = window
17385        .update(cx, |editor, _window, cx| {
17386            (
17387                editor.buffer().clone(),
17388                editor.buffer().read(cx).snapshot(cx),
17389            )
17390        })
17391        .unwrap();
17392
17393    let edits = edits
17394        .into_iter()
17395        .map(|(range, edit)| {
17396            (
17397                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17398                edit,
17399            )
17400        })
17401        .collect::<Vec<_>>();
17402
17403    let text_anchor_edits = edits
17404        .clone()
17405        .into_iter()
17406        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17407        .collect::<Vec<_>>();
17408
17409    let edit_preview = window
17410        .update(cx, |_, _window, cx| {
17411            buffer
17412                .read(cx)
17413                .as_singleton()
17414                .unwrap()
17415                .read(cx)
17416                .preview_edits(text_anchor_edits.into(), cx)
17417        })
17418        .unwrap()
17419        .await;
17420
17421    cx.update(|_window, cx| {
17422        let highlighted_edits = inline_completion_edit_text(
17423            &snapshot.as_singleton().unwrap().2,
17424            &edits,
17425            &edit_preview,
17426            include_deletions,
17427            cx,
17428        );
17429        assertion_fn(highlighted_edits, cx)
17430    });
17431}
17432
17433#[track_caller]
17434fn assert_breakpoint(
17435    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17436    path: &Arc<Path>,
17437    expected: Vec<(u32, Breakpoint)>,
17438) {
17439    if expected.len() == 0usize {
17440        assert!(!breakpoints.contains_key(path), "{}", path.display());
17441    } else {
17442        let mut breakpoint = breakpoints
17443            .get(path)
17444            .unwrap()
17445            .into_iter()
17446            .map(|breakpoint| {
17447                (
17448                    breakpoint.row,
17449                    Breakpoint {
17450                        message: breakpoint.message.clone(),
17451                        state: breakpoint.state,
17452                        condition: breakpoint.condition.clone(),
17453                        hit_condition: breakpoint.hit_condition.clone(),
17454                    },
17455                )
17456            })
17457            .collect::<Vec<_>>();
17458
17459        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17460
17461        assert_eq!(expected, breakpoint);
17462    }
17463}
17464
17465fn add_log_breakpoint_at_cursor(
17466    editor: &mut Editor,
17467    log_message: &str,
17468    window: &mut Window,
17469    cx: &mut Context<Editor>,
17470) {
17471    let (anchor, bp) = editor
17472        .breakpoint_at_cursor_head(window, cx)
17473        .unwrap_or_else(|| {
17474            let cursor_position: Point = editor.selections.newest(cx).head();
17475
17476            let breakpoint_position = editor
17477                .snapshot(window, cx)
17478                .display_snapshot
17479                .buffer_snapshot
17480                .anchor_before(Point::new(cursor_position.row, 0));
17481
17482            (breakpoint_position, Breakpoint::new_log(&log_message))
17483        });
17484
17485    editor.edit_breakpoint_at_anchor(
17486        anchor,
17487        bp,
17488        BreakpointEditAction::EditLogMessage(log_message.into()),
17489        cx,
17490    );
17491}
17492
17493#[gpui::test]
17494async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17495    init_test(cx, |_| {});
17496
17497    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17498    let fs = FakeFs::new(cx.executor());
17499    fs.insert_tree(
17500        path!("/a"),
17501        json!({
17502            "main.rs": sample_text,
17503        }),
17504    )
17505    .await;
17506    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17507    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17508    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17509
17510    let fs = FakeFs::new(cx.executor());
17511    fs.insert_tree(
17512        path!("/a"),
17513        json!({
17514            "main.rs": sample_text,
17515        }),
17516    )
17517    .await;
17518    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17519    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17520    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17521    let worktree_id = workspace
17522        .update(cx, |workspace, _window, cx| {
17523            workspace.project().update(cx, |project, cx| {
17524                project.worktrees(cx).next().unwrap().read(cx).id()
17525            })
17526        })
17527        .unwrap();
17528
17529    let buffer = project
17530        .update(cx, |project, cx| {
17531            project.open_buffer((worktree_id, "main.rs"), cx)
17532        })
17533        .await
17534        .unwrap();
17535
17536    let (editor, cx) = cx.add_window_view(|window, cx| {
17537        Editor::new(
17538            EditorMode::Full,
17539            MultiBuffer::build_from_buffer(buffer, cx),
17540            Some(project.clone()),
17541            window,
17542            cx,
17543        )
17544    });
17545
17546    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17547    let abs_path = project.read_with(cx, |project, cx| {
17548        project
17549            .absolute_path(&project_path, cx)
17550            .map(|path_buf| Arc::from(path_buf.to_owned()))
17551            .unwrap()
17552    });
17553
17554    // assert we can add breakpoint on the first line
17555    editor.update_in(cx, |editor, window, cx| {
17556        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17557        editor.move_to_end(&MoveToEnd, window, cx);
17558        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17559    });
17560
17561    let breakpoints = editor.update(cx, |editor, cx| {
17562        editor
17563            .breakpoint_store()
17564            .as_ref()
17565            .unwrap()
17566            .read(cx)
17567            .all_breakpoints(cx)
17568            .clone()
17569    });
17570
17571    assert_eq!(1, breakpoints.len());
17572    assert_breakpoint(
17573        &breakpoints,
17574        &abs_path,
17575        vec![
17576            (0, Breakpoint::new_standard()),
17577            (3, Breakpoint::new_standard()),
17578        ],
17579    );
17580
17581    editor.update_in(cx, |editor, window, cx| {
17582        editor.move_to_beginning(&MoveToBeginning, window, cx);
17583        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17584    });
17585
17586    let breakpoints = editor.update(cx, |editor, cx| {
17587        editor
17588            .breakpoint_store()
17589            .as_ref()
17590            .unwrap()
17591            .read(cx)
17592            .all_breakpoints(cx)
17593            .clone()
17594    });
17595
17596    assert_eq!(1, breakpoints.len());
17597    assert_breakpoint(
17598        &breakpoints,
17599        &abs_path,
17600        vec![(3, Breakpoint::new_standard())],
17601    );
17602
17603    editor.update_in(cx, |editor, window, cx| {
17604        editor.move_to_end(&MoveToEnd, window, cx);
17605        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17606    });
17607
17608    let breakpoints = editor.update(cx, |editor, cx| {
17609        editor
17610            .breakpoint_store()
17611            .as_ref()
17612            .unwrap()
17613            .read(cx)
17614            .all_breakpoints(cx)
17615            .clone()
17616    });
17617
17618    assert_eq!(0, breakpoints.len());
17619    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17620}
17621
17622#[gpui::test]
17623async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17624    init_test(cx, |_| {});
17625
17626    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17627
17628    let fs = FakeFs::new(cx.executor());
17629    fs.insert_tree(
17630        path!("/a"),
17631        json!({
17632            "main.rs": sample_text,
17633        }),
17634    )
17635    .await;
17636    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17637    let (workspace, cx) =
17638        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17639
17640    let worktree_id = workspace.update(cx, |workspace, cx| {
17641        workspace.project().update(cx, |project, cx| {
17642            project.worktrees(cx).next().unwrap().read(cx).id()
17643        })
17644    });
17645
17646    let buffer = project
17647        .update(cx, |project, cx| {
17648            project.open_buffer((worktree_id, "main.rs"), cx)
17649        })
17650        .await
17651        .unwrap();
17652
17653    let (editor, cx) = cx.add_window_view(|window, cx| {
17654        Editor::new(
17655            EditorMode::Full,
17656            MultiBuffer::build_from_buffer(buffer, cx),
17657            Some(project.clone()),
17658            window,
17659            cx,
17660        )
17661    });
17662
17663    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17664    let abs_path = project.read_with(cx, |project, cx| {
17665        project
17666            .absolute_path(&project_path, cx)
17667            .map(|path_buf| Arc::from(path_buf.to_owned()))
17668            .unwrap()
17669    });
17670
17671    editor.update_in(cx, |editor, window, cx| {
17672        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17673    });
17674
17675    let breakpoints = editor.update(cx, |editor, cx| {
17676        editor
17677            .breakpoint_store()
17678            .as_ref()
17679            .unwrap()
17680            .read(cx)
17681            .all_breakpoints(cx)
17682            .clone()
17683    });
17684
17685    assert_breakpoint(
17686        &breakpoints,
17687        &abs_path,
17688        vec![(0, Breakpoint::new_log("hello world"))],
17689    );
17690
17691    // Removing a log message from a log breakpoint should remove it
17692    editor.update_in(cx, |editor, window, cx| {
17693        add_log_breakpoint_at_cursor(editor, "", window, cx);
17694    });
17695
17696    let breakpoints = editor.update(cx, |editor, cx| {
17697        editor
17698            .breakpoint_store()
17699            .as_ref()
17700            .unwrap()
17701            .read(cx)
17702            .all_breakpoints(cx)
17703            .clone()
17704    });
17705
17706    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17707
17708    editor.update_in(cx, |editor, window, cx| {
17709        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17710        editor.move_to_end(&MoveToEnd, window, cx);
17711        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17712        // Not adding a log message to a standard breakpoint shouldn't remove it
17713        add_log_breakpoint_at_cursor(editor, "", window, cx);
17714    });
17715
17716    let breakpoints = editor.update(cx, |editor, cx| {
17717        editor
17718            .breakpoint_store()
17719            .as_ref()
17720            .unwrap()
17721            .read(cx)
17722            .all_breakpoints(cx)
17723            .clone()
17724    });
17725
17726    assert_breakpoint(
17727        &breakpoints,
17728        &abs_path,
17729        vec![
17730            (0, Breakpoint::new_standard()),
17731            (3, Breakpoint::new_standard()),
17732        ],
17733    );
17734
17735    editor.update_in(cx, |editor, window, cx| {
17736        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17737    });
17738
17739    let breakpoints = editor.update(cx, |editor, cx| {
17740        editor
17741            .breakpoint_store()
17742            .as_ref()
17743            .unwrap()
17744            .read(cx)
17745            .all_breakpoints(cx)
17746            .clone()
17747    });
17748
17749    assert_breakpoint(
17750        &breakpoints,
17751        &abs_path,
17752        vec![
17753            (0, Breakpoint::new_standard()),
17754            (3, Breakpoint::new_log("hello world")),
17755        ],
17756    );
17757
17758    editor.update_in(cx, |editor, window, cx| {
17759        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17760    });
17761
17762    let breakpoints = editor.update(cx, |editor, cx| {
17763        editor
17764            .breakpoint_store()
17765            .as_ref()
17766            .unwrap()
17767            .read(cx)
17768            .all_breakpoints(cx)
17769            .clone()
17770    });
17771
17772    assert_breakpoint(
17773        &breakpoints,
17774        &abs_path,
17775        vec![
17776            (0, Breakpoint::new_standard()),
17777            (3, Breakpoint::new_log("hello Earth!!")),
17778        ],
17779    );
17780}
17781
17782/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17783/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17784/// or when breakpoints were placed out of order. This tests for a regression too
17785#[gpui::test]
17786async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17787    init_test(cx, |_| {});
17788
17789    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17790    let fs = FakeFs::new(cx.executor());
17791    fs.insert_tree(
17792        path!("/a"),
17793        json!({
17794            "main.rs": sample_text,
17795        }),
17796    )
17797    .await;
17798    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17799    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17800    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17801
17802    let fs = FakeFs::new(cx.executor());
17803    fs.insert_tree(
17804        path!("/a"),
17805        json!({
17806            "main.rs": sample_text,
17807        }),
17808    )
17809    .await;
17810    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17811    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17812    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17813    let worktree_id = workspace
17814        .update(cx, |workspace, _window, cx| {
17815            workspace.project().update(cx, |project, cx| {
17816                project.worktrees(cx).next().unwrap().read(cx).id()
17817            })
17818        })
17819        .unwrap();
17820
17821    let buffer = project
17822        .update(cx, |project, cx| {
17823            project.open_buffer((worktree_id, "main.rs"), cx)
17824        })
17825        .await
17826        .unwrap();
17827
17828    let (editor, cx) = cx.add_window_view(|window, cx| {
17829        Editor::new(
17830            EditorMode::Full,
17831            MultiBuffer::build_from_buffer(buffer, cx),
17832            Some(project.clone()),
17833            window,
17834            cx,
17835        )
17836    });
17837
17838    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17839    let abs_path = project.read_with(cx, |project, cx| {
17840        project
17841            .absolute_path(&project_path, cx)
17842            .map(|path_buf| Arc::from(path_buf.to_owned()))
17843            .unwrap()
17844    });
17845
17846    // assert we can add breakpoint on the first line
17847    editor.update_in(cx, |editor, window, cx| {
17848        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17849        editor.move_to_end(&MoveToEnd, window, cx);
17850        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17851        editor.move_up(&MoveUp, window, cx);
17852        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17853    });
17854
17855    let breakpoints = editor.update(cx, |editor, cx| {
17856        editor
17857            .breakpoint_store()
17858            .as_ref()
17859            .unwrap()
17860            .read(cx)
17861            .all_breakpoints(cx)
17862            .clone()
17863    });
17864
17865    assert_eq!(1, breakpoints.len());
17866    assert_breakpoint(
17867        &breakpoints,
17868        &abs_path,
17869        vec![
17870            (0, Breakpoint::new_standard()),
17871            (2, Breakpoint::new_standard()),
17872            (3, Breakpoint::new_standard()),
17873        ],
17874    );
17875
17876    editor.update_in(cx, |editor, window, cx| {
17877        editor.move_to_beginning(&MoveToBeginning, window, cx);
17878        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17879        editor.move_to_end(&MoveToEnd, window, cx);
17880        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17881        // Disabling a breakpoint that doesn't exist should do nothing
17882        editor.move_up(&MoveUp, window, cx);
17883        editor.move_up(&MoveUp, window, cx);
17884        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17885    });
17886
17887    let breakpoints = editor.update(cx, |editor, cx| {
17888        editor
17889            .breakpoint_store()
17890            .as_ref()
17891            .unwrap()
17892            .read(cx)
17893            .all_breakpoints(cx)
17894            .clone()
17895    });
17896
17897    let disable_breakpoint = {
17898        let mut bp = Breakpoint::new_standard();
17899        bp.state = BreakpointState::Disabled;
17900        bp
17901    };
17902
17903    assert_eq!(1, breakpoints.len());
17904    assert_breakpoint(
17905        &breakpoints,
17906        &abs_path,
17907        vec![
17908            (0, disable_breakpoint.clone()),
17909            (2, Breakpoint::new_standard()),
17910            (3, disable_breakpoint.clone()),
17911        ],
17912    );
17913
17914    editor.update_in(cx, |editor, window, cx| {
17915        editor.move_to_beginning(&MoveToBeginning, window, cx);
17916        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17917        editor.move_to_end(&MoveToEnd, window, cx);
17918        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17919        editor.move_up(&MoveUp, window, cx);
17920        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17921    });
17922
17923    let breakpoints = editor.update(cx, |editor, cx| {
17924        editor
17925            .breakpoint_store()
17926            .as_ref()
17927            .unwrap()
17928            .read(cx)
17929            .all_breakpoints(cx)
17930            .clone()
17931    });
17932
17933    assert_eq!(1, breakpoints.len());
17934    assert_breakpoint(
17935        &breakpoints,
17936        &abs_path,
17937        vec![
17938            (0, Breakpoint::new_standard()),
17939            (2, disable_breakpoint),
17940            (3, Breakpoint::new_standard()),
17941        ],
17942    );
17943}
17944
17945#[gpui::test]
17946async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17947    init_test(cx, |_| {});
17948    let capabilities = lsp::ServerCapabilities {
17949        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17950            prepare_provider: Some(true),
17951            work_done_progress_options: Default::default(),
17952        })),
17953        ..Default::default()
17954    };
17955    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17956
17957    cx.set_state(indoc! {"
17958        struct Fˇoo {}
17959    "});
17960
17961    cx.update_editor(|editor, _, cx| {
17962        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17963        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17964        editor.highlight_background::<DocumentHighlightRead>(
17965            &[highlight_range],
17966            |c| c.editor_document_highlight_read_background,
17967            cx,
17968        );
17969    });
17970
17971    let mut prepare_rename_handler = cx
17972        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17973            move |_, _, _| async move {
17974                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17975                    start: lsp::Position {
17976                        line: 0,
17977                        character: 7,
17978                    },
17979                    end: lsp::Position {
17980                        line: 0,
17981                        character: 10,
17982                    },
17983                })))
17984            },
17985        );
17986    let prepare_rename_task = cx
17987        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17988        .expect("Prepare rename was not started");
17989    prepare_rename_handler.next().await.unwrap();
17990    prepare_rename_task.await.expect("Prepare rename failed");
17991
17992    let mut rename_handler =
17993        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17994            let edit = lsp::TextEdit {
17995                range: lsp::Range {
17996                    start: lsp::Position {
17997                        line: 0,
17998                        character: 7,
17999                    },
18000                    end: lsp::Position {
18001                        line: 0,
18002                        character: 10,
18003                    },
18004                },
18005                new_text: "FooRenamed".to_string(),
18006            };
18007            Ok(Some(lsp::WorkspaceEdit::new(
18008                // Specify the same edit twice
18009                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18010            )))
18011        });
18012    let rename_task = cx
18013        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18014        .expect("Confirm rename was not started");
18015    rename_handler.next().await.unwrap();
18016    rename_task.await.expect("Confirm rename failed");
18017    cx.run_until_parked();
18018
18019    // Despite two edits, only one is actually applied as those are identical
18020    cx.assert_editor_state(indoc! {"
18021        struct FooRenamedˇ {}
18022    "});
18023}
18024
18025#[gpui::test]
18026async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18027    init_test(cx, |_| {});
18028    // These capabilities indicate that the server does not support prepare rename.
18029    let capabilities = lsp::ServerCapabilities {
18030        rename_provider: Some(lsp::OneOf::Left(true)),
18031        ..Default::default()
18032    };
18033    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18034
18035    cx.set_state(indoc! {"
18036        struct Fˇoo {}
18037    "});
18038
18039    cx.update_editor(|editor, _window, cx| {
18040        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18041        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18042        editor.highlight_background::<DocumentHighlightRead>(
18043            &[highlight_range],
18044            |c| c.editor_document_highlight_read_background,
18045            cx,
18046        );
18047    });
18048
18049    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18050        .expect("Prepare rename was not started")
18051        .await
18052        .expect("Prepare rename failed");
18053
18054    let mut rename_handler =
18055        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18056            let edit = lsp::TextEdit {
18057                range: lsp::Range {
18058                    start: lsp::Position {
18059                        line: 0,
18060                        character: 7,
18061                    },
18062                    end: lsp::Position {
18063                        line: 0,
18064                        character: 10,
18065                    },
18066                },
18067                new_text: "FooRenamed".to_string(),
18068            };
18069            Ok(Some(lsp::WorkspaceEdit::new(
18070                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18071            )))
18072        });
18073    let rename_task = cx
18074        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18075        .expect("Confirm rename was not started");
18076    rename_handler.next().await.unwrap();
18077    rename_task.await.expect("Confirm rename failed");
18078    cx.run_until_parked();
18079
18080    // Correct range is renamed, as `surrounding_word` is used to find it.
18081    cx.assert_editor_state(indoc! {"
18082        struct FooRenamedˇ {}
18083    "});
18084}
18085
18086#[gpui::test]
18087async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18088    init_test(cx, |_| {});
18089    let mut cx = EditorTestContext::new(cx).await;
18090
18091    let language = Arc::new(
18092        Language::new(
18093            LanguageConfig::default(),
18094            Some(tree_sitter_html::LANGUAGE.into()),
18095        )
18096        .with_brackets_query(
18097            r#"
18098            ("<" @open "/>" @close)
18099            ("</" @open ">" @close)
18100            ("<" @open ">" @close)
18101            ("\"" @open "\"" @close)
18102            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18103        "#,
18104        )
18105        .unwrap(),
18106    );
18107    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18108
18109    cx.set_state(indoc! {"
18110        <span>ˇ</span>
18111    "});
18112    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18113    cx.assert_editor_state(indoc! {"
18114        <span>
18115        ˇ
18116        </span>
18117    "});
18118
18119    cx.set_state(indoc! {"
18120        <span><span></span>ˇ</span>
18121    "});
18122    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18123    cx.assert_editor_state(indoc! {"
18124        <span><span></span>
18125        ˇ</span>
18126    "});
18127
18128    cx.set_state(indoc! {"
18129        <span>ˇ
18130        </span>
18131    "});
18132    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18133    cx.assert_editor_state(indoc! {"
18134        <span>
18135        ˇ
18136        </span>
18137    "});
18138}
18139
18140#[gpui::test(iterations = 10)]
18141async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18142    init_test(cx, |_| {});
18143
18144    let fs = FakeFs::new(cx.executor());
18145    fs.insert_tree(
18146        path!("/dir"),
18147        json!({
18148            "a.ts": "a",
18149        }),
18150    )
18151    .await;
18152
18153    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18154    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18155    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18156
18157    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18158    language_registry.add(Arc::new(Language::new(
18159        LanguageConfig {
18160            name: "TypeScript".into(),
18161            matcher: LanguageMatcher {
18162                path_suffixes: vec!["ts".to_string()],
18163                ..Default::default()
18164            },
18165            ..Default::default()
18166        },
18167        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18168    )));
18169    let mut fake_language_servers = language_registry.register_fake_lsp(
18170        "TypeScript",
18171        FakeLspAdapter {
18172            capabilities: lsp::ServerCapabilities {
18173                code_lens_provider: Some(lsp::CodeLensOptions {
18174                    resolve_provider: Some(true),
18175                }),
18176                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18177                    commands: vec!["_the/command".to_string()],
18178                    ..lsp::ExecuteCommandOptions::default()
18179                }),
18180                ..lsp::ServerCapabilities::default()
18181            },
18182            ..FakeLspAdapter::default()
18183        },
18184    );
18185
18186    let (buffer, _handle) = project
18187        .update(cx, |p, cx| {
18188            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18189        })
18190        .await
18191        .unwrap();
18192    cx.executor().run_until_parked();
18193
18194    let fake_server = fake_language_servers.next().await.unwrap();
18195
18196    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18197    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18198    drop(buffer_snapshot);
18199    let actions = cx
18200        .update_window(*workspace, |_, window, cx| {
18201            project.code_actions(&buffer, anchor..anchor, window, cx)
18202        })
18203        .unwrap();
18204
18205    fake_server
18206        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18207            Ok(Some(vec![
18208                lsp::CodeLens {
18209                    range: lsp::Range::default(),
18210                    command: Some(lsp::Command {
18211                        title: "Code lens command".to_owned(),
18212                        command: "_the/command".to_owned(),
18213                        arguments: None,
18214                    }),
18215                    data: None,
18216                },
18217                lsp::CodeLens {
18218                    range: lsp::Range::default(),
18219                    command: Some(lsp::Command {
18220                        title: "Command not in capabilities".to_owned(),
18221                        command: "not in capabilities".to_owned(),
18222                        arguments: None,
18223                    }),
18224                    data: None,
18225                },
18226                lsp::CodeLens {
18227                    range: lsp::Range {
18228                        start: lsp::Position {
18229                            line: 1,
18230                            character: 1,
18231                        },
18232                        end: lsp::Position {
18233                            line: 1,
18234                            character: 1,
18235                        },
18236                    },
18237                    command: Some(lsp::Command {
18238                        title: "Command not in range".to_owned(),
18239                        command: "_the/command".to_owned(),
18240                        arguments: None,
18241                    }),
18242                    data: None,
18243                },
18244            ]))
18245        })
18246        .next()
18247        .await;
18248
18249    let actions = actions.await.unwrap();
18250    assert_eq!(
18251        actions.len(),
18252        1,
18253        "Should have only one valid action for the 0..0 range"
18254    );
18255    let action = actions[0].clone();
18256    let apply = project.update(cx, |project, cx| {
18257        project.apply_code_action(buffer.clone(), action, true, cx)
18258    });
18259
18260    // Resolving the code action does not populate its edits. In absence of
18261    // edits, we must execute the given command.
18262    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18263        |mut lens, _| async move {
18264            let lens_command = lens.command.as_mut().expect("should have a command");
18265            assert_eq!(lens_command.title, "Code lens command");
18266            lens_command.arguments = Some(vec![json!("the-argument")]);
18267            Ok(lens)
18268        },
18269    );
18270
18271    // While executing the command, the language server sends the editor
18272    // a `workspaceEdit` request.
18273    fake_server
18274        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18275            let fake = fake_server.clone();
18276            move |params, _| {
18277                assert_eq!(params.command, "_the/command");
18278                let fake = fake.clone();
18279                async move {
18280                    fake.server
18281                        .request::<lsp::request::ApplyWorkspaceEdit>(
18282                            lsp::ApplyWorkspaceEditParams {
18283                                label: None,
18284                                edit: lsp::WorkspaceEdit {
18285                                    changes: Some(
18286                                        [(
18287                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18288                                            vec![lsp::TextEdit {
18289                                                range: lsp::Range::new(
18290                                                    lsp::Position::new(0, 0),
18291                                                    lsp::Position::new(0, 0),
18292                                                ),
18293                                                new_text: "X".into(),
18294                                            }],
18295                                        )]
18296                                        .into_iter()
18297                                        .collect(),
18298                                    ),
18299                                    ..Default::default()
18300                                },
18301                            },
18302                        )
18303                        .await
18304                        .unwrap();
18305                    Ok(Some(json!(null)))
18306                }
18307            }
18308        })
18309        .next()
18310        .await;
18311
18312    // Applying the code lens command returns a project transaction containing the edits
18313    // sent by the language server in its `workspaceEdit` request.
18314    let transaction = apply.await.unwrap();
18315    assert!(transaction.0.contains_key(&buffer));
18316    buffer.update(cx, |buffer, cx| {
18317        assert_eq!(buffer.text(), "Xa");
18318        buffer.undo(cx);
18319        assert_eq!(buffer.text(), "a");
18320    });
18321}
18322
18323#[gpui::test]
18324async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18325    init_test(cx, |_| {});
18326
18327    let fs = FakeFs::new(cx.executor());
18328    let main_text = r#"fn main() {
18329println!("1");
18330println!("2");
18331println!("3");
18332println!("4");
18333println!("5");
18334}"#;
18335    let lib_text = "mod foo {}";
18336    fs.insert_tree(
18337        path!("/a"),
18338        json!({
18339            "lib.rs": lib_text,
18340            "main.rs": main_text,
18341        }),
18342    )
18343    .await;
18344
18345    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18346    let (workspace, cx) =
18347        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18348    let worktree_id = workspace.update(cx, |workspace, cx| {
18349        workspace.project().update(cx, |project, cx| {
18350            project.worktrees(cx).next().unwrap().read(cx).id()
18351        })
18352    });
18353
18354    let expected_ranges = vec![
18355        Point::new(0, 0)..Point::new(0, 0),
18356        Point::new(1, 0)..Point::new(1, 1),
18357        Point::new(2, 0)..Point::new(2, 2),
18358        Point::new(3, 0)..Point::new(3, 3),
18359    ];
18360
18361    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18362    let editor_1 = workspace
18363        .update_in(cx, |workspace, window, cx| {
18364            workspace.open_path(
18365                (worktree_id, "main.rs"),
18366                Some(pane_1.downgrade()),
18367                true,
18368                window,
18369                cx,
18370            )
18371        })
18372        .unwrap()
18373        .await
18374        .downcast::<Editor>()
18375        .unwrap();
18376    pane_1.update(cx, |pane, cx| {
18377        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18378        open_editor.update(cx, |editor, cx| {
18379            assert_eq!(
18380                editor.display_text(cx),
18381                main_text,
18382                "Original main.rs text on initial open",
18383            );
18384            assert_eq!(
18385                editor
18386                    .selections
18387                    .all::<Point>(cx)
18388                    .into_iter()
18389                    .map(|s| s.range())
18390                    .collect::<Vec<_>>(),
18391                vec![Point::zero()..Point::zero()],
18392                "Default selections on initial open",
18393            );
18394        })
18395    });
18396    editor_1.update_in(cx, |editor, window, cx| {
18397        editor.change_selections(None, window, cx, |s| {
18398            s.select_ranges(expected_ranges.clone());
18399        });
18400    });
18401
18402    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18403        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18404    });
18405    let editor_2 = workspace
18406        .update_in(cx, |workspace, window, cx| {
18407            workspace.open_path(
18408                (worktree_id, "main.rs"),
18409                Some(pane_2.downgrade()),
18410                true,
18411                window,
18412                cx,
18413            )
18414        })
18415        .unwrap()
18416        .await
18417        .downcast::<Editor>()
18418        .unwrap();
18419    pane_2.update(cx, |pane, cx| {
18420        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18421        open_editor.update(cx, |editor, cx| {
18422            assert_eq!(
18423                editor.display_text(cx),
18424                main_text,
18425                "Original main.rs text on initial open in another panel",
18426            );
18427            assert_eq!(
18428                editor
18429                    .selections
18430                    .all::<Point>(cx)
18431                    .into_iter()
18432                    .map(|s| s.range())
18433                    .collect::<Vec<_>>(),
18434                vec![Point::zero()..Point::zero()],
18435                "Default selections on initial open in another panel",
18436            );
18437        })
18438    });
18439
18440    editor_2.update_in(cx, |editor, window, cx| {
18441        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18442    });
18443
18444    let _other_editor_1 = workspace
18445        .update_in(cx, |workspace, window, cx| {
18446            workspace.open_path(
18447                (worktree_id, "lib.rs"),
18448                Some(pane_1.downgrade()),
18449                true,
18450                window,
18451                cx,
18452            )
18453        })
18454        .unwrap()
18455        .await
18456        .downcast::<Editor>()
18457        .unwrap();
18458    pane_1
18459        .update_in(cx, |pane, window, cx| {
18460            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18461                .unwrap()
18462        })
18463        .await
18464        .unwrap();
18465    drop(editor_1);
18466    pane_1.update(cx, |pane, cx| {
18467        pane.active_item()
18468            .unwrap()
18469            .downcast::<Editor>()
18470            .unwrap()
18471            .update(cx, |editor, cx| {
18472                assert_eq!(
18473                    editor.display_text(cx),
18474                    lib_text,
18475                    "Other file should be open and active",
18476                );
18477            });
18478        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18479    });
18480
18481    let _other_editor_2 = workspace
18482        .update_in(cx, |workspace, window, cx| {
18483            workspace.open_path(
18484                (worktree_id, "lib.rs"),
18485                Some(pane_2.downgrade()),
18486                true,
18487                window,
18488                cx,
18489            )
18490        })
18491        .unwrap()
18492        .await
18493        .downcast::<Editor>()
18494        .unwrap();
18495    pane_2
18496        .update_in(cx, |pane, window, cx| {
18497            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18498                .unwrap()
18499        })
18500        .await
18501        .unwrap();
18502    drop(editor_2);
18503    pane_2.update(cx, |pane, cx| {
18504        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18505        open_editor.update(cx, |editor, cx| {
18506            assert_eq!(
18507                editor.display_text(cx),
18508                lib_text,
18509                "Other file should be open and active in another panel too",
18510            );
18511        });
18512        assert_eq!(
18513            pane.items().count(),
18514            1,
18515            "No other editors should be open in another pane",
18516        );
18517    });
18518
18519    let _editor_1_reopened = workspace
18520        .update_in(cx, |workspace, window, cx| {
18521            workspace.open_path(
18522                (worktree_id, "main.rs"),
18523                Some(pane_1.downgrade()),
18524                true,
18525                window,
18526                cx,
18527            )
18528        })
18529        .unwrap()
18530        .await
18531        .downcast::<Editor>()
18532        .unwrap();
18533    let _editor_2_reopened = workspace
18534        .update_in(cx, |workspace, window, cx| {
18535            workspace.open_path(
18536                (worktree_id, "main.rs"),
18537                Some(pane_2.downgrade()),
18538                true,
18539                window,
18540                cx,
18541            )
18542        })
18543        .unwrap()
18544        .await
18545        .downcast::<Editor>()
18546        .unwrap();
18547    pane_1.update(cx, |pane, cx| {
18548        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18549        open_editor.update(cx, |editor, cx| {
18550            assert_eq!(
18551                editor.display_text(cx),
18552                main_text,
18553                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18554            );
18555            assert_eq!(
18556                editor
18557                    .selections
18558                    .all::<Point>(cx)
18559                    .into_iter()
18560                    .map(|s| s.range())
18561                    .collect::<Vec<_>>(),
18562                expected_ranges,
18563                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18564            );
18565        })
18566    });
18567    pane_2.update(cx, |pane, cx| {
18568        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18569        open_editor.update(cx, |editor, cx| {
18570            assert_eq!(
18571                editor.display_text(cx),
18572                r#"fn main() {
18573⋯rintln!("1");
18574⋯intln!("2");
18575⋯ntln!("3");
18576println!("4");
18577println!("5");
18578}"#,
18579                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18580            );
18581            assert_eq!(
18582                editor
18583                    .selections
18584                    .all::<Point>(cx)
18585                    .into_iter()
18586                    .map(|s| s.range())
18587                    .collect::<Vec<_>>(),
18588                vec![Point::zero()..Point::zero()],
18589                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18590            );
18591        })
18592    });
18593}
18594
18595#[gpui::test]
18596async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18597    init_test(cx, |_| {});
18598
18599    let fs = FakeFs::new(cx.executor());
18600    let main_text = r#"fn main() {
18601println!("1");
18602println!("2");
18603println!("3");
18604println!("4");
18605println!("5");
18606}"#;
18607    let lib_text = "mod foo {}";
18608    fs.insert_tree(
18609        path!("/a"),
18610        json!({
18611            "lib.rs": lib_text,
18612            "main.rs": main_text,
18613        }),
18614    )
18615    .await;
18616
18617    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18618    let (workspace, cx) =
18619        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18620    let worktree_id = workspace.update(cx, |workspace, cx| {
18621        workspace.project().update(cx, |project, cx| {
18622            project.worktrees(cx).next().unwrap().read(cx).id()
18623        })
18624    });
18625
18626    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18627    let editor = workspace
18628        .update_in(cx, |workspace, window, cx| {
18629            workspace.open_path(
18630                (worktree_id, "main.rs"),
18631                Some(pane.downgrade()),
18632                true,
18633                window,
18634                cx,
18635            )
18636        })
18637        .unwrap()
18638        .await
18639        .downcast::<Editor>()
18640        .unwrap();
18641    pane.update(cx, |pane, cx| {
18642        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18643        open_editor.update(cx, |editor, cx| {
18644            assert_eq!(
18645                editor.display_text(cx),
18646                main_text,
18647                "Original main.rs text on initial open",
18648            );
18649        })
18650    });
18651    editor.update_in(cx, |editor, window, cx| {
18652        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18653    });
18654
18655    cx.update_global(|store: &mut SettingsStore, cx| {
18656        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18657            s.restore_on_file_reopen = Some(false);
18658        });
18659    });
18660    editor.update_in(cx, |editor, window, cx| {
18661        editor.fold_ranges(
18662            vec![
18663                Point::new(1, 0)..Point::new(1, 1),
18664                Point::new(2, 0)..Point::new(2, 2),
18665                Point::new(3, 0)..Point::new(3, 3),
18666            ],
18667            false,
18668            window,
18669            cx,
18670        );
18671    });
18672    pane.update_in(cx, |pane, window, cx| {
18673        pane.close_all_items(&CloseAllItems::default(), window, cx)
18674            .unwrap()
18675    })
18676    .await
18677    .unwrap();
18678    pane.update(cx, |pane, _| {
18679        assert!(pane.active_item().is_none());
18680    });
18681    cx.update_global(|store: &mut SettingsStore, cx| {
18682        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18683            s.restore_on_file_reopen = Some(true);
18684        });
18685    });
18686
18687    let _editor_reopened = workspace
18688        .update_in(cx, |workspace, window, cx| {
18689            workspace.open_path(
18690                (worktree_id, "main.rs"),
18691                Some(pane.downgrade()),
18692                true,
18693                window,
18694                cx,
18695            )
18696        })
18697        .unwrap()
18698        .await
18699        .downcast::<Editor>()
18700        .unwrap();
18701    pane.update(cx, |pane, cx| {
18702        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18703        open_editor.update(cx, |editor, cx| {
18704            assert_eq!(
18705                editor.display_text(cx),
18706                main_text,
18707                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18708            );
18709        })
18710    });
18711}
18712
18713fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18714    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18715    point..point
18716}
18717
18718fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18719    let (text, ranges) = marked_text_ranges(marked_text, true);
18720    assert_eq!(editor.text(cx), text);
18721    assert_eq!(
18722        editor.selections.ranges(cx),
18723        ranges,
18724        "Assert selections are {}",
18725        marked_text
18726    );
18727}
18728
18729pub fn handle_signature_help_request(
18730    cx: &mut EditorLspTestContext,
18731    mocked_response: lsp::SignatureHelp,
18732) -> impl Future<Output = ()> + use<> {
18733    let mut request =
18734        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18735            let mocked_response = mocked_response.clone();
18736            async move { Ok(Some(mocked_response)) }
18737        });
18738
18739    async move {
18740        request.next().await;
18741    }
18742}
18743
18744/// Handle completion request passing a marked string specifying where the completion
18745/// should be triggered from using '|' character, what range should be replaced, and what completions
18746/// should be returned using '<' and '>' to delimit the range.
18747///
18748/// Also see `handle_completion_request_with_insert_and_replace`.
18749#[track_caller]
18750pub fn handle_completion_request(
18751    cx: &mut EditorLspTestContext,
18752    marked_string: &str,
18753    completions: Vec<&'static str>,
18754    counter: Arc<AtomicUsize>,
18755) -> impl Future<Output = ()> {
18756    let complete_from_marker: TextRangeMarker = '|'.into();
18757    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18758    let (_, mut marked_ranges) = marked_text_ranges_by(
18759        marked_string,
18760        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18761    );
18762
18763    let complete_from_position =
18764        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18765    let replace_range =
18766        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18767
18768    let mut request =
18769        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18770            let completions = completions.clone();
18771            counter.fetch_add(1, atomic::Ordering::Release);
18772            async move {
18773                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18774                assert_eq!(
18775                    params.text_document_position.position,
18776                    complete_from_position
18777                );
18778                Ok(Some(lsp::CompletionResponse::Array(
18779                    completions
18780                        .iter()
18781                        .map(|completion_text| lsp::CompletionItem {
18782                            label: completion_text.to_string(),
18783                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18784                                range: replace_range,
18785                                new_text: completion_text.to_string(),
18786                            })),
18787                            ..Default::default()
18788                        })
18789                        .collect(),
18790                )))
18791            }
18792        });
18793
18794    async move {
18795        request.next().await;
18796    }
18797}
18798
18799/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18800/// given instead, which also contains an `insert` range.
18801///
18802/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18803/// that is, `replace_range.start..cursor_pos`.
18804pub fn handle_completion_request_with_insert_and_replace(
18805    cx: &mut EditorLspTestContext,
18806    marked_string: &str,
18807    completions: Vec<&'static str>,
18808    counter: Arc<AtomicUsize>,
18809) -> impl Future<Output = ()> {
18810    let complete_from_marker: TextRangeMarker = '|'.into();
18811    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18812    let (_, mut marked_ranges) = marked_text_ranges_by(
18813        marked_string,
18814        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18815    );
18816
18817    let complete_from_position =
18818        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18819    let replace_range =
18820        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18821
18822    let mut request =
18823        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18824            let completions = completions.clone();
18825            counter.fetch_add(1, atomic::Ordering::Release);
18826            async move {
18827                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18828                assert_eq!(
18829                    params.text_document_position.position, complete_from_position,
18830                    "marker `|` position doesn't match",
18831                );
18832                Ok(Some(lsp::CompletionResponse::Array(
18833                    completions
18834                        .iter()
18835                        .map(|completion_text| lsp::CompletionItem {
18836                            label: completion_text.to_string(),
18837                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18838                                lsp::InsertReplaceEdit {
18839                                    insert: lsp::Range {
18840                                        start: replace_range.start,
18841                                        end: complete_from_position,
18842                                    },
18843                                    replace: replace_range,
18844                                    new_text: completion_text.to_string(),
18845                                },
18846                            )),
18847                            ..Default::default()
18848                        })
18849                        .collect(),
18850                )))
18851            }
18852        });
18853
18854    async move {
18855        request.next().await;
18856    }
18857}
18858
18859fn handle_resolve_completion_request(
18860    cx: &mut EditorLspTestContext,
18861    edits: Option<Vec<(&'static str, &'static str)>>,
18862) -> impl Future<Output = ()> {
18863    let edits = edits.map(|edits| {
18864        edits
18865            .iter()
18866            .map(|(marked_string, new_text)| {
18867                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18868                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18869                lsp::TextEdit::new(replace_range, new_text.to_string())
18870            })
18871            .collect::<Vec<_>>()
18872    });
18873
18874    let mut request =
18875        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18876            let edits = edits.clone();
18877            async move {
18878                Ok(lsp::CompletionItem {
18879                    additional_text_edits: edits,
18880                    ..Default::default()
18881                })
18882            }
18883        });
18884
18885    async move {
18886        request.next().await;
18887    }
18888}
18889
18890pub(crate) fn update_test_language_settings(
18891    cx: &mut TestAppContext,
18892    f: impl Fn(&mut AllLanguageSettingsContent),
18893) {
18894    cx.update(|cx| {
18895        SettingsStore::update_global(cx, |store, cx| {
18896            store.update_user_settings::<AllLanguageSettings>(cx, f);
18897        });
18898    });
18899}
18900
18901pub(crate) fn update_test_project_settings(
18902    cx: &mut TestAppContext,
18903    f: impl Fn(&mut ProjectSettings),
18904) {
18905    cx.update(|cx| {
18906        SettingsStore::update_global(cx, |store, cx| {
18907            store.update_user_settings::<ProjectSettings>(cx, f);
18908        });
18909    });
18910}
18911
18912pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18913    cx.update(|cx| {
18914        assets::Assets.load_test_fonts(cx);
18915        let store = SettingsStore::test(cx);
18916        cx.set_global(store);
18917        theme::init(theme::LoadThemes::JustBase, cx);
18918        release_channel::init(SemanticVersion::default(), cx);
18919        client::init_settings(cx);
18920        language::init(cx);
18921        Project::init_settings(cx);
18922        workspace::init_settings(cx);
18923        crate::init(cx);
18924    });
18925
18926    update_test_language_settings(cx, f);
18927}
18928
18929#[track_caller]
18930fn assert_hunk_revert(
18931    not_reverted_text_with_selections: &str,
18932    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18933    expected_reverted_text_with_selections: &str,
18934    base_text: &str,
18935    cx: &mut EditorLspTestContext,
18936) {
18937    cx.set_state(not_reverted_text_with_selections);
18938    cx.set_head_text(base_text);
18939    cx.executor().run_until_parked();
18940
18941    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18942        let snapshot = editor.snapshot(window, cx);
18943        let reverted_hunk_statuses = snapshot
18944            .buffer_snapshot
18945            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18946            .map(|hunk| hunk.status().kind)
18947            .collect::<Vec<_>>();
18948
18949        editor.git_restore(&Default::default(), window, cx);
18950        reverted_hunk_statuses
18951    });
18952    cx.executor().run_until_parked();
18953    cx.assert_editor_state(expected_reverted_text_with_selections);
18954    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18955}