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: 1,
 4238                render: Arc::new(|_| div().into_any()),
 4239                priority: 0,
 4240            }],
 4241            Some(Autoscroll::fit()),
 4242            cx,
 4243        );
 4244        editor.change_selections(None, window, cx, |s| {
 4245            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4246        });
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248    });
 4249}
 4250
 4251#[gpui::test]
 4252async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4253    init_test(cx, |_| {});
 4254
 4255    let mut cx = EditorTestContext::new(cx).await;
 4256    cx.set_state(
 4257        &"
 4258            ˇzero
 4259            one
 4260            two
 4261            three
 4262            four
 4263            five
 4264        "
 4265        .unindent(),
 4266    );
 4267
 4268    // Create a four-line block that replaces three lines of text.
 4269    cx.update_editor(|editor, window, cx| {
 4270        let snapshot = editor.snapshot(window, cx);
 4271        let snapshot = &snapshot.buffer_snapshot;
 4272        let placement = BlockPlacement::Replace(
 4273            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4274        );
 4275        editor.insert_blocks(
 4276            [BlockProperties {
 4277                placement,
 4278                height: 4,
 4279                style: BlockStyle::Sticky,
 4280                render: Arc::new(|_| gpui::div().into_any_element()),
 4281                priority: 0,
 4282            }],
 4283            None,
 4284            cx,
 4285        );
 4286    });
 4287
 4288    // Move down so that the cursor touches the block.
 4289    cx.update_editor(|editor, window, cx| {
 4290        editor.move_down(&Default::default(), window, cx);
 4291    });
 4292    cx.assert_editor_state(
 4293        &"
 4294            zero
 4295            «one
 4296            two
 4297            threeˇ»
 4298            four
 4299            five
 4300        "
 4301        .unindent(),
 4302    );
 4303
 4304    // Move down past the block.
 4305    cx.update_editor(|editor, window, cx| {
 4306        editor.move_down(&Default::default(), window, cx);
 4307    });
 4308    cx.assert_editor_state(
 4309        &"
 4310            zero
 4311            one
 4312            two
 4313            three
 4314            ˇfour
 4315            five
 4316        "
 4317        .unindent(),
 4318    );
 4319}
 4320
 4321#[gpui::test]
 4322fn test_transpose(cx: &mut TestAppContext) {
 4323    init_test(cx, |_| {});
 4324
 4325    _ = cx.add_window(|window, cx| {
 4326        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4327        editor.set_style(EditorStyle::default(), window, cx);
 4328        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4329        editor.transpose(&Default::default(), window, cx);
 4330        assert_eq!(editor.text(cx), "bac");
 4331        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4332
 4333        editor.transpose(&Default::default(), window, cx);
 4334        assert_eq!(editor.text(cx), "bca");
 4335        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4336
 4337        editor.transpose(&Default::default(), window, cx);
 4338        assert_eq!(editor.text(cx), "bac");
 4339        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4340
 4341        editor
 4342    });
 4343
 4344    _ = cx.add_window(|window, cx| {
 4345        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4346        editor.set_style(EditorStyle::default(), window, cx);
 4347        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4348        editor.transpose(&Default::default(), window, cx);
 4349        assert_eq!(editor.text(cx), "acb\nde");
 4350        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4351
 4352        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4353        editor.transpose(&Default::default(), window, cx);
 4354        assert_eq!(editor.text(cx), "acbd\ne");
 4355        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4356
 4357        editor.transpose(&Default::default(), window, cx);
 4358        assert_eq!(editor.text(cx), "acbde\n");
 4359        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4360
 4361        editor.transpose(&Default::default(), window, cx);
 4362        assert_eq!(editor.text(cx), "acbd\ne");
 4363        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4364
 4365        editor
 4366    });
 4367
 4368    _ = cx.add_window(|window, cx| {
 4369        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4370        editor.set_style(EditorStyle::default(), window, cx);
 4371        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4372        editor.transpose(&Default::default(), window, cx);
 4373        assert_eq!(editor.text(cx), "bacd\ne");
 4374        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4375
 4376        editor.transpose(&Default::default(), window, cx);
 4377        assert_eq!(editor.text(cx), "bcade\n");
 4378        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4379
 4380        editor.transpose(&Default::default(), window, cx);
 4381        assert_eq!(editor.text(cx), "bcda\ne");
 4382        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4383
 4384        editor.transpose(&Default::default(), window, cx);
 4385        assert_eq!(editor.text(cx), "bcade\n");
 4386        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4387
 4388        editor.transpose(&Default::default(), window, cx);
 4389        assert_eq!(editor.text(cx), "bcaed\n");
 4390        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4391
 4392        editor
 4393    });
 4394
 4395    _ = cx.add_window(|window, cx| {
 4396        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4397        editor.set_style(EditorStyle::default(), window, cx);
 4398        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4399        editor.transpose(&Default::default(), window, cx);
 4400        assert_eq!(editor.text(cx), "🏀🍐✋");
 4401        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4402
 4403        editor.transpose(&Default::default(), window, cx);
 4404        assert_eq!(editor.text(cx), "🏀✋🍐");
 4405        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4406
 4407        editor.transpose(&Default::default(), window, cx);
 4408        assert_eq!(editor.text(cx), "🏀🍐✋");
 4409        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4410
 4411        editor
 4412    });
 4413}
 4414
 4415#[gpui::test]
 4416async fn test_rewrap(cx: &mut TestAppContext) {
 4417    init_test(cx, |settings| {
 4418        settings.languages.extend([
 4419            (
 4420                "Markdown".into(),
 4421                LanguageSettingsContent {
 4422                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4423                    ..Default::default()
 4424                },
 4425            ),
 4426            (
 4427                "Plain Text".into(),
 4428                LanguageSettingsContent {
 4429                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4430                    ..Default::default()
 4431                },
 4432            ),
 4433        ])
 4434    });
 4435
 4436    let mut cx = EditorTestContext::new(cx).await;
 4437
 4438    let language_with_c_comments = Arc::new(Language::new(
 4439        LanguageConfig {
 4440            line_comments: vec!["// ".into()],
 4441            ..LanguageConfig::default()
 4442        },
 4443        None,
 4444    ));
 4445    let language_with_pound_comments = Arc::new(Language::new(
 4446        LanguageConfig {
 4447            line_comments: vec!["# ".into()],
 4448            ..LanguageConfig::default()
 4449        },
 4450        None,
 4451    ));
 4452    let markdown_language = Arc::new(Language::new(
 4453        LanguageConfig {
 4454            name: "Markdown".into(),
 4455            ..LanguageConfig::default()
 4456        },
 4457        None,
 4458    ));
 4459    let language_with_doc_comments = Arc::new(Language::new(
 4460        LanguageConfig {
 4461            line_comments: vec!["// ".into(), "/// ".into()],
 4462            ..LanguageConfig::default()
 4463        },
 4464        Some(tree_sitter_rust::LANGUAGE.into()),
 4465    ));
 4466
 4467    let plaintext_language = Arc::new(Language::new(
 4468        LanguageConfig {
 4469            name: "Plain Text".into(),
 4470            ..LanguageConfig::default()
 4471        },
 4472        None,
 4473    ));
 4474
 4475    assert_rewrap(
 4476        indoc! {"
 4477            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4478        "},
 4479        indoc! {"
 4480            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4481            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4482            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4483            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4484            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4485            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4486            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4487            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4488            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4489            // porttitor id. Aliquam id accumsan eros.
 4490        "},
 4491        language_with_c_comments.clone(),
 4492        &mut cx,
 4493    );
 4494
 4495    // Test that rewrapping works inside of a selection
 4496    assert_rewrap(
 4497        indoc! {"
 4498            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4499        "},
 4500        indoc! {"
 4501            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4502            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4503            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4504            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4505            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4506            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4507            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4508            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4509            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4510            // porttitor id. Aliquam id accumsan eros.ˇ»
 4511        "},
 4512        language_with_c_comments.clone(),
 4513        &mut cx,
 4514    );
 4515
 4516    // Test that cursors that expand to the same region are collapsed.
 4517    assert_rewrap(
 4518        indoc! {"
 4519            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4520            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4521            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4522            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4523        "},
 4524        indoc! {"
 4525            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4526            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4527            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4528            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4529            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4530            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4531            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4532            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4533            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4534            // porttitor id. Aliquam id accumsan eros.
 4535        "},
 4536        language_with_c_comments.clone(),
 4537        &mut cx,
 4538    );
 4539
 4540    // Test that non-contiguous selections are treated separately.
 4541    assert_rewrap(
 4542        indoc! {"
 4543            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4544            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4545            //
 4546            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4547            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        indoc! {"
 4550            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4551            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4552            // auctor, eu lacinia sapien scelerisque.
 4553            //
 4554            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4555            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4556            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4557            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4558            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4559            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4560            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4561        "},
 4562        language_with_c_comments.clone(),
 4563        &mut cx,
 4564    );
 4565
 4566    // Test that different comment prefixes are supported.
 4567    assert_rewrap(
 4568        indoc! {"
 4569            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4570        "},
 4571        indoc! {"
 4572            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4573            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4574            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4575            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4576            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4577            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4578            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4579            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4580            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4581            # accumsan eros.
 4582        "},
 4583        language_with_pound_comments.clone(),
 4584        &mut cx,
 4585    );
 4586
 4587    // Test that rewrapping is ignored outside of comments in most languages.
 4588    assert_rewrap(
 4589        indoc! {"
 4590            /// Adds two numbers.
 4591            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4592            fn add(a: u32, b: u32) -> u32 {
 4593                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4594            }
 4595        "},
 4596        indoc! {"
 4597            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4598            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4599            fn add(a: u32, b: u32) -> u32 {
 4600                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4601            }
 4602        "},
 4603        language_with_doc_comments.clone(),
 4604        &mut cx,
 4605    );
 4606
 4607    // Test that rewrapping works in Markdown and Plain Text languages.
 4608    assert_rewrap(
 4609        indoc! {"
 4610            # Hello
 4611
 4612            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4613        "},
 4614        indoc! {"
 4615            # Hello
 4616
 4617            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4618            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4619            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4620            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4621            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4622            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4623            Integer sit amet scelerisque nisi.
 4624        "},
 4625        markdown_language,
 4626        &mut cx,
 4627    );
 4628
 4629    assert_rewrap(
 4630        indoc! {"
 4631            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4632        "},
 4633        indoc! {"
 4634            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4635            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4636            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4637            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4638            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4639            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4640            Integer sit amet scelerisque nisi.
 4641        "},
 4642        plaintext_language,
 4643        &mut cx,
 4644    );
 4645
 4646    // Test rewrapping unaligned comments in a selection.
 4647    assert_rewrap(
 4648        indoc! {"
 4649            fn foo() {
 4650                if true {
 4651            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4652            // Praesent semper egestas tellus id dignissim.ˇ»
 4653                    do_something();
 4654                } else {
 4655                    //
 4656                }
 4657            }
 4658        "},
 4659        indoc! {"
 4660            fn foo() {
 4661                if true {
 4662            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4663                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4664                    // egestas tellus id dignissim.ˇ»
 4665                    do_something();
 4666                } else {
 4667                    //
 4668                }
 4669            }
 4670        "},
 4671        language_with_doc_comments.clone(),
 4672        &mut cx,
 4673    );
 4674
 4675    assert_rewrap(
 4676        indoc! {"
 4677            fn foo() {
 4678                if true {
 4679            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4680            // Praesent semper egestas tellus id dignissim.»
 4681                    do_something();
 4682                } else {
 4683                    //
 4684                }
 4685
 4686            }
 4687        "},
 4688        indoc! {"
 4689            fn foo() {
 4690                if true {
 4691            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4692                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4693                    // egestas tellus id dignissim.»
 4694                    do_something();
 4695                } else {
 4696                    //
 4697                }
 4698
 4699            }
 4700        "},
 4701        language_with_doc_comments.clone(),
 4702        &mut cx,
 4703    );
 4704
 4705    #[track_caller]
 4706    fn assert_rewrap(
 4707        unwrapped_text: &str,
 4708        wrapped_text: &str,
 4709        language: Arc<Language>,
 4710        cx: &mut EditorTestContext,
 4711    ) {
 4712        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4713        cx.set_state(unwrapped_text);
 4714        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4715        cx.assert_editor_state(wrapped_text);
 4716    }
 4717}
 4718
 4719#[gpui::test]
 4720async fn test_hard_wrap(cx: &mut TestAppContext) {
 4721    init_test(cx, |_| {});
 4722    let mut cx = EditorTestContext::new(cx).await;
 4723
 4724    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4725    cx.update_editor(|editor, _, cx| {
 4726        editor.set_hard_wrap(Some(14), cx);
 4727    });
 4728
 4729    cx.set_state(indoc!(
 4730        "
 4731        one two three ˇ
 4732        "
 4733    ));
 4734    cx.simulate_input("four");
 4735    cx.run_until_parked();
 4736
 4737    cx.assert_editor_state(indoc!(
 4738        "
 4739        one two three
 4740        fourˇ
 4741        "
 4742    ));
 4743
 4744    cx.update_editor(|editor, window, cx| {
 4745        editor.newline(&Default::default(), window, cx);
 4746    });
 4747    cx.run_until_parked();
 4748    cx.assert_editor_state(indoc!(
 4749        "
 4750        one two three
 4751        four
 4752        ˇ
 4753        "
 4754    ));
 4755
 4756    cx.simulate_input("five");
 4757    cx.run_until_parked();
 4758    cx.assert_editor_state(indoc!(
 4759        "
 4760        one two three
 4761        four
 4762        fiveˇ
 4763        "
 4764    ));
 4765
 4766    cx.update_editor(|editor, window, cx| {
 4767        editor.newline(&Default::default(), window, cx);
 4768    });
 4769    cx.run_until_parked();
 4770    cx.simulate_input("# ");
 4771    cx.run_until_parked();
 4772    cx.assert_editor_state(indoc!(
 4773        "
 4774        one two three
 4775        four
 4776        five
 4777        # ˇ
 4778        "
 4779    ));
 4780
 4781    cx.update_editor(|editor, window, cx| {
 4782        editor.newline(&Default::default(), window, cx);
 4783    });
 4784    cx.run_until_parked();
 4785    cx.assert_editor_state(indoc!(
 4786        "
 4787        one two three
 4788        four
 4789        five
 4790        #\x20
 4791 4792        "
 4793    ));
 4794
 4795    cx.simulate_input(" 6");
 4796    cx.run_until_parked();
 4797    cx.assert_editor_state(indoc!(
 4798        "
 4799        one two three
 4800        four
 4801        five
 4802        #
 4803        # 6ˇ
 4804        "
 4805    ));
 4806}
 4807
 4808#[gpui::test]
 4809async fn test_clipboard(cx: &mut TestAppContext) {
 4810    init_test(cx, |_| {});
 4811
 4812    let mut cx = EditorTestContext::new(cx).await;
 4813
 4814    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4815    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4816    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4817
 4818    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4819    cx.set_state("two ˇfour ˇsix ˇ");
 4820    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4821    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4822
 4823    // Paste again but with only two cursors. Since the number of cursors doesn't
 4824    // match the number of slices in the clipboard, the entire clipboard text
 4825    // is pasted at each cursor.
 4826    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4827    cx.update_editor(|e, window, cx| {
 4828        e.handle_input("( ", window, cx);
 4829        e.paste(&Paste, window, cx);
 4830        e.handle_input(") ", window, cx);
 4831    });
 4832    cx.assert_editor_state(
 4833        &([
 4834            "( one✅ ",
 4835            "three ",
 4836            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4837            "three ",
 4838            "five ) ˇ",
 4839        ]
 4840        .join("\n")),
 4841    );
 4842
 4843    // Cut with three selections, one of which is full-line.
 4844    cx.set_state(indoc! {"
 4845        1«2ˇ»3
 4846        4ˇ567
 4847        «8ˇ»9"});
 4848    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4849    cx.assert_editor_state(indoc! {"
 4850        1ˇ3
 4851        ˇ9"});
 4852
 4853    // Paste with three selections, noticing how the copied selection that was full-line
 4854    // gets inserted before the second cursor.
 4855    cx.set_state(indoc! {"
 4856        1ˇ3
 4857 4858        «oˇ»ne"});
 4859    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4860    cx.assert_editor_state(indoc! {"
 4861        12ˇ3
 4862        4567
 4863 4864        8ˇne"});
 4865
 4866    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4867    cx.set_state(indoc! {"
 4868        The quick brown
 4869        fox juˇmps over
 4870        the lazy dog"});
 4871    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4872    assert_eq!(
 4873        cx.read_from_clipboard()
 4874            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4875        Some("fox jumps over\n".to_string())
 4876    );
 4877
 4878    // Paste with three selections, noticing how the copied full-line selection is inserted
 4879    // before the empty selections but replaces the selection that is non-empty.
 4880    cx.set_state(indoc! {"
 4881        Tˇhe quick brown
 4882        «foˇ»x jumps over
 4883        tˇhe lazy dog"});
 4884    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4885    cx.assert_editor_state(indoc! {"
 4886        fox jumps over
 4887        Tˇhe quick brown
 4888        fox jumps over
 4889        ˇx jumps over
 4890        fox jumps over
 4891        tˇhe lazy dog"});
 4892}
 4893
 4894#[gpui::test]
 4895async fn test_copy_trim(cx: &mut TestAppContext) {
 4896    init_test(cx, |_| {});
 4897
 4898    let mut cx = EditorTestContext::new(cx).await;
 4899    cx.set_state(
 4900        r#"            «for selection in selections.iter() {
 4901            let mut start = selection.start;
 4902            let mut end = selection.end;
 4903            let is_entire_line = selection.is_empty();
 4904            if is_entire_line {
 4905                start = Point::new(start.row, 0);ˇ»
 4906                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4907            }
 4908        "#,
 4909    );
 4910    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4911    assert_eq!(
 4912        cx.read_from_clipboard()
 4913            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4914        Some(
 4915            "for selection in selections.iter() {
 4916            let mut start = selection.start;
 4917            let mut end = selection.end;
 4918            let is_entire_line = selection.is_empty();
 4919            if is_entire_line {
 4920                start = Point::new(start.row, 0);"
 4921                .to_string()
 4922        ),
 4923        "Regular copying preserves all indentation selected",
 4924    );
 4925    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4926    assert_eq!(
 4927        cx.read_from_clipboard()
 4928            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4929        Some(
 4930            "for selection in selections.iter() {
 4931let mut start = selection.start;
 4932let mut end = selection.end;
 4933let is_entire_line = selection.is_empty();
 4934if is_entire_line {
 4935    start = Point::new(start.row, 0);"
 4936                .to_string()
 4937        ),
 4938        "Copying with stripping should strip all leading whitespaces"
 4939    );
 4940
 4941    cx.set_state(
 4942        r#"       «     for selection in selections.iter() {
 4943            let mut start = selection.start;
 4944            let mut end = selection.end;
 4945            let is_entire_line = selection.is_empty();
 4946            if is_entire_line {
 4947                start = Point::new(start.row, 0);ˇ»
 4948                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4949            }
 4950        "#,
 4951    );
 4952    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4953    assert_eq!(
 4954        cx.read_from_clipboard()
 4955            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4956        Some(
 4957            "     for selection in selections.iter() {
 4958            let mut start = selection.start;
 4959            let mut end = selection.end;
 4960            let is_entire_line = selection.is_empty();
 4961            if is_entire_line {
 4962                start = Point::new(start.row, 0);"
 4963                .to_string()
 4964        ),
 4965        "Regular copying preserves all indentation selected",
 4966    );
 4967    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4968    assert_eq!(
 4969        cx.read_from_clipboard()
 4970            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4971        Some(
 4972            "for selection in selections.iter() {
 4973let mut start = selection.start;
 4974let mut end = selection.end;
 4975let is_entire_line = selection.is_empty();
 4976if is_entire_line {
 4977    start = Point::new(start.row, 0);"
 4978                .to_string()
 4979        ),
 4980        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 4981    );
 4982
 4983    cx.set_state(
 4984        r#"       «ˇ     for selection in selections.iter() {
 4985            let mut start = selection.start;
 4986            let mut end = selection.end;
 4987            let is_entire_line = selection.is_empty();
 4988            if is_entire_line {
 4989                start = Point::new(start.row, 0);»
 4990                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4991            }
 4992        "#,
 4993    );
 4994    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4995    assert_eq!(
 4996        cx.read_from_clipboard()
 4997            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4998        Some(
 4999            "     for selection in selections.iter() {
 5000            let mut start = selection.start;
 5001            let mut end = selection.end;
 5002            let is_entire_line = selection.is_empty();
 5003            if is_entire_line {
 5004                start = Point::new(start.row, 0);"
 5005                .to_string()
 5006        ),
 5007        "Regular copying for reverse selection works the same",
 5008    );
 5009    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5010    assert_eq!(
 5011        cx.read_from_clipboard()
 5012            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5013        Some(
 5014            "for selection in selections.iter() {
 5015let mut start = selection.start;
 5016let mut end = selection.end;
 5017let is_entire_line = selection.is_empty();
 5018if is_entire_line {
 5019    start = Point::new(start.row, 0);"
 5020                .to_string()
 5021        ),
 5022        "Copying with stripping for reverse selection works the same"
 5023    );
 5024
 5025    cx.set_state(
 5026        r#"            for selection «in selections.iter() {
 5027            let mut start = selection.start;
 5028            let mut end = selection.end;
 5029            let is_entire_line = selection.is_empty();
 5030            if is_entire_line {
 5031                start = Point::new(start.row, 0);ˇ»
 5032                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5033            }
 5034        "#,
 5035    );
 5036    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5037    assert_eq!(
 5038        cx.read_from_clipboard()
 5039            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5040        Some(
 5041            "in selections.iter() {
 5042            let mut start = selection.start;
 5043            let mut end = selection.end;
 5044            let is_entire_line = selection.is_empty();
 5045            if is_entire_line {
 5046                start = Point::new(start.row, 0);"
 5047                .to_string()
 5048        ),
 5049        "When selecting past the indent, the copying works as usual",
 5050    );
 5051    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5052    assert_eq!(
 5053        cx.read_from_clipboard()
 5054            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5055        Some(
 5056            "in selections.iter() {
 5057            let mut start = selection.start;
 5058            let mut end = selection.end;
 5059            let is_entire_line = selection.is_empty();
 5060            if is_entire_line {
 5061                start = Point::new(start.row, 0);"
 5062                .to_string()
 5063        ),
 5064        "When selecting past the indent, nothing is trimmed"
 5065    );
 5066}
 5067
 5068#[gpui::test]
 5069async fn test_paste_multiline(cx: &mut TestAppContext) {
 5070    init_test(cx, |_| {});
 5071
 5072    let mut cx = EditorTestContext::new(cx).await;
 5073    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5074
 5075    // Cut an indented block, without the leading whitespace.
 5076    cx.set_state(indoc! {"
 5077        const a: B = (
 5078            c(),
 5079            «d(
 5080                e,
 5081                f
 5082            )ˇ»
 5083        );
 5084    "});
 5085    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5086    cx.assert_editor_state(indoc! {"
 5087        const a: B = (
 5088            c(),
 5089            ˇ
 5090        );
 5091    "});
 5092
 5093    // Paste it at the same position.
 5094    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5095    cx.assert_editor_state(indoc! {"
 5096        const a: B = (
 5097            c(),
 5098            d(
 5099                e,
 5100                f
 5101 5102        );
 5103    "});
 5104
 5105    // Paste it at a line with a lower indent level.
 5106    cx.set_state(indoc! {"
 5107        ˇ
 5108        const a: B = (
 5109            c(),
 5110        );
 5111    "});
 5112    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5113    cx.assert_editor_state(indoc! {"
 5114        d(
 5115            e,
 5116            f
 5117 5118        const a: B = (
 5119            c(),
 5120        );
 5121    "});
 5122
 5123    // Cut an indented block, with the leading whitespace.
 5124    cx.set_state(indoc! {"
 5125        const a: B = (
 5126            c(),
 5127        «    d(
 5128                e,
 5129                f
 5130            )
 5131        ˇ»);
 5132    "});
 5133    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5134    cx.assert_editor_state(indoc! {"
 5135        const a: B = (
 5136            c(),
 5137        ˇ);
 5138    "});
 5139
 5140    // Paste it at the same position.
 5141    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5142    cx.assert_editor_state(indoc! {"
 5143        const a: B = (
 5144            c(),
 5145            d(
 5146                e,
 5147                f
 5148            )
 5149        ˇ);
 5150    "});
 5151
 5152    // Paste it at a line with a higher indent level.
 5153    cx.set_state(indoc! {"
 5154        const a: B = (
 5155            c(),
 5156            d(
 5157                e,
 5158 5159            )
 5160        );
 5161    "});
 5162    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5163    cx.assert_editor_state(indoc! {"
 5164        const a: B = (
 5165            c(),
 5166            d(
 5167                e,
 5168                f    d(
 5169                    e,
 5170                    f
 5171                )
 5172        ˇ
 5173            )
 5174        );
 5175    "});
 5176
 5177    // Copy an indented block, starting mid-line
 5178    cx.set_state(indoc! {"
 5179        const a: B = (
 5180            c(),
 5181            somethin«g(
 5182                e,
 5183                f
 5184            )ˇ»
 5185        );
 5186    "});
 5187    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5188
 5189    // Paste it on a line with a lower indent level
 5190    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5191    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5192    cx.assert_editor_state(indoc! {"
 5193        const a: B = (
 5194            c(),
 5195            something(
 5196                e,
 5197                f
 5198            )
 5199        );
 5200        g(
 5201            e,
 5202            f
 5203"});
 5204}
 5205
 5206#[gpui::test]
 5207async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5208    init_test(cx, |_| {});
 5209
 5210    cx.write_to_clipboard(ClipboardItem::new_string(
 5211        "    d(\n        e\n    );\n".into(),
 5212    ));
 5213
 5214    let mut cx = EditorTestContext::new(cx).await;
 5215    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5216
 5217    cx.set_state(indoc! {"
 5218        fn a() {
 5219            b();
 5220            if c() {
 5221                ˇ
 5222            }
 5223        }
 5224    "});
 5225
 5226    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5227    cx.assert_editor_state(indoc! {"
 5228        fn a() {
 5229            b();
 5230            if c() {
 5231                d(
 5232                    e
 5233                );
 5234        ˇ
 5235            }
 5236        }
 5237    "});
 5238
 5239    cx.set_state(indoc! {"
 5240        fn a() {
 5241            b();
 5242            ˇ
 5243        }
 5244    "});
 5245
 5246    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5247    cx.assert_editor_state(indoc! {"
 5248        fn a() {
 5249            b();
 5250            d(
 5251                e
 5252            );
 5253        ˇ
 5254        }
 5255    "});
 5256}
 5257
 5258#[gpui::test]
 5259fn test_select_all(cx: &mut TestAppContext) {
 5260    init_test(cx, |_| {});
 5261
 5262    let editor = cx.add_window(|window, cx| {
 5263        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5264        build_editor(buffer, window, cx)
 5265    });
 5266    _ = editor.update(cx, |editor, window, cx| {
 5267        editor.select_all(&SelectAll, window, cx);
 5268        assert_eq!(
 5269            editor.selections.display_ranges(cx),
 5270            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5271        );
 5272    });
 5273}
 5274
 5275#[gpui::test]
 5276fn test_select_line(cx: &mut TestAppContext) {
 5277    init_test(cx, |_| {});
 5278
 5279    let editor = cx.add_window(|window, cx| {
 5280        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5281        build_editor(buffer, window, cx)
 5282    });
 5283    _ = editor.update(cx, |editor, window, cx| {
 5284        editor.change_selections(None, window, cx, |s| {
 5285            s.select_display_ranges([
 5286                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5287                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5288                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5289                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5290            ])
 5291        });
 5292        editor.select_line(&SelectLine, window, cx);
 5293        assert_eq!(
 5294            editor.selections.display_ranges(cx),
 5295            vec![
 5296                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5297                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5298            ]
 5299        );
 5300    });
 5301
 5302    _ = editor.update(cx, |editor, window, cx| {
 5303        editor.select_line(&SelectLine, window, cx);
 5304        assert_eq!(
 5305            editor.selections.display_ranges(cx),
 5306            vec![
 5307                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5308                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5309            ]
 5310        );
 5311    });
 5312
 5313    _ = editor.update(cx, |editor, window, cx| {
 5314        editor.select_line(&SelectLine, window, cx);
 5315        assert_eq!(
 5316            editor.selections.display_ranges(cx),
 5317            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5318        );
 5319    });
 5320}
 5321
 5322#[gpui::test]
 5323async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5324    init_test(cx, |_| {});
 5325    let mut cx = EditorTestContext::new(cx).await;
 5326
 5327    #[track_caller]
 5328    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5329        cx.set_state(initial_state);
 5330        cx.update_editor(|e, window, cx| {
 5331            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5332        });
 5333        cx.assert_editor_state(expected_state);
 5334    }
 5335
 5336    // Selection starts and ends at the middle of lines, left-to-right
 5337    test(
 5338        &mut cx,
 5339        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5340        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5341    );
 5342    // Same thing, right-to-left
 5343    test(
 5344        &mut cx,
 5345        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5346        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5347    );
 5348
 5349    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5350    test(
 5351        &mut cx,
 5352        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5353        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5354    );
 5355    // Same thing, right-to-left
 5356    test(
 5357        &mut cx,
 5358        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5359        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5360    );
 5361
 5362    // Whole buffer, left-to-right, last line ends with newline
 5363    test(
 5364        &mut cx,
 5365        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5366        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5367    );
 5368    // Same thing, right-to-left
 5369    test(
 5370        &mut cx,
 5371        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5372        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5373    );
 5374
 5375    // Starts at the end of a line, ends at the start of another
 5376    test(
 5377        &mut cx,
 5378        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5379        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5380    );
 5381}
 5382
 5383#[gpui::test]
 5384async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5385    init_test(cx, |_| {});
 5386
 5387    let editor = cx.add_window(|window, cx| {
 5388        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5389        build_editor(buffer, window, cx)
 5390    });
 5391
 5392    // setup
 5393    _ = editor.update(cx, |editor, window, cx| {
 5394        editor.fold_creases(
 5395            vec![
 5396                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5397                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5398                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5399            ],
 5400            true,
 5401            window,
 5402            cx,
 5403        );
 5404        assert_eq!(
 5405            editor.display_text(cx),
 5406            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5407        );
 5408    });
 5409
 5410    _ = editor.update(cx, |editor, window, cx| {
 5411        editor.change_selections(None, window, cx, |s| {
 5412            s.select_display_ranges([
 5413                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5414                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5415                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5416                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5417            ])
 5418        });
 5419        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5420        assert_eq!(
 5421            editor.display_text(cx),
 5422            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5423        );
 5424    });
 5425    EditorTestContext::for_editor(editor, cx)
 5426        .await
 5427        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5428
 5429    _ = editor.update(cx, |editor, window, cx| {
 5430        editor.change_selections(None, window, cx, |s| {
 5431            s.select_display_ranges([
 5432                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5433            ])
 5434        });
 5435        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5436        assert_eq!(
 5437            editor.display_text(cx),
 5438            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5439        );
 5440        assert_eq!(
 5441            editor.selections.display_ranges(cx),
 5442            [
 5443                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5444                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5445                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5446                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5447                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5448                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5449                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5450            ]
 5451        );
 5452    });
 5453    EditorTestContext::for_editor(editor, cx)
 5454        .await
 5455        .assert_editor_state(
 5456            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5457        );
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let mut cx = EditorTestContext::new(cx).await;
 5465
 5466    cx.set_state(indoc!(
 5467        r#"abc
 5468           defˇghi
 5469
 5470           jk
 5471           nlmo
 5472           "#
 5473    ));
 5474
 5475    cx.update_editor(|editor, window, cx| {
 5476        editor.add_selection_above(&Default::default(), window, cx);
 5477    });
 5478
 5479    cx.assert_editor_state(indoc!(
 5480        r#"abcˇ
 5481           defˇghi
 5482
 5483           jk
 5484           nlmo
 5485           "#
 5486    ));
 5487
 5488    cx.update_editor(|editor, window, cx| {
 5489        editor.add_selection_above(&Default::default(), window, cx);
 5490    });
 5491
 5492    cx.assert_editor_state(indoc!(
 5493        r#"abcˇ
 5494            defˇghi
 5495
 5496            jk
 5497            nlmo
 5498            "#
 5499    ));
 5500
 5501    cx.update_editor(|editor, window, cx| {
 5502        editor.add_selection_below(&Default::default(), window, cx);
 5503    });
 5504
 5505    cx.assert_editor_state(indoc!(
 5506        r#"abc
 5507           defˇghi
 5508
 5509           jk
 5510           nlmo
 5511           "#
 5512    ));
 5513
 5514    cx.update_editor(|editor, window, cx| {
 5515        editor.undo_selection(&Default::default(), window, cx);
 5516    });
 5517
 5518    cx.assert_editor_state(indoc!(
 5519        r#"abcˇ
 5520           defˇghi
 5521
 5522           jk
 5523           nlmo
 5524           "#
 5525    ));
 5526
 5527    cx.update_editor(|editor, window, cx| {
 5528        editor.redo_selection(&Default::default(), window, cx);
 5529    });
 5530
 5531    cx.assert_editor_state(indoc!(
 5532        r#"abc
 5533           defˇghi
 5534
 5535           jk
 5536           nlmo
 5537           "#
 5538    ));
 5539
 5540    cx.update_editor(|editor, window, cx| {
 5541        editor.add_selection_below(&Default::default(), window, cx);
 5542    });
 5543
 5544    cx.assert_editor_state(indoc!(
 5545        r#"abc
 5546           defˇghi
 5547
 5548           jk
 5549           nlmˇo
 5550           "#
 5551    ));
 5552
 5553    cx.update_editor(|editor, window, cx| {
 5554        editor.add_selection_below(&Default::default(), window, cx);
 5555    });
 5556
 5557    cx.assert_editor_state(indoc!(
 5558        r#"abc
 5559           defˇghi
 5560
 5561           jk
 5562           nlmˇo
 5563           "#
 5564    ));
 5565
 5566    // change selections
 5567    cx.set_state(indoc!(
 5568        r#"abc
 5569           def«ˇg»hi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_below(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abc
 5582           def«ˇg»hi
 5583
 5584           jk
 5585           nlm«ˇo»
 5586           "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           def«ˇg»hi
 5596
 5597           jk
 5598           nlm«ˇo»
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.add_selection_above(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abc
 5608           def«ˇg»hi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.add_selection_above(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           def«ˇg»hi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    // Change selections again
 5629    cx.set_state(indoc!(
 5630        r#"a«bc
 5631           defgˇ»hi
 5632
 5633           jk
 5634           nlmo
 5635           "#
 5636    ));
 5637
 5638    cx.update_editor(|editor, window, cx| {
 5639        editor.add_selection_below(&Default::default(), window, cx);
 5640    });
 5641
 5642    cx.assert_editor_state(indoc!(
 5643        r#"a«bcˇ»
 5644           d«efgˇ»hi
 5645
 5646           j«kˇ»
 5647           nlmo
 5648           "#
 5649    ));
 5650
 5651    cx.update_editor(|editor, window, cx| {
 5652        editor.add_selection_below(&Default::default(), window, cx);
 5653    });
 5654    cx.assert_editor_state(indoc!(
 5655        r#"a«bcˇ»
 5656           d«efgˇ»hi
 5657
 5658           j«kˇ»
 5659           n«lmoˇ»
 5660           "#
 5661    ));
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_above(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"a«bcˇ»
 5668           d«efgˇ»hi
 5669
 5670           j«kˇ»
 5671           nlmo
 5672           "#
 5673    ));
 5674
 5675    // Change selections again
 5676    cx.set_state(indoc!(
 5677        r#"abc
 5678           d«ˇefghi
 5679
 5680           jk
 5681           nlm»o
 5682           "#
 5683    ));
 5684
 5685    cx.update_editor(|editor, window, cx| {
 5686        editor.add_selection_above(&Default::default(), window, cx);
 5687    });
 5688
 5689    cx.assert_editor_state(indoc!(
 5690        r#"a«ˇbc»
 5691           d«ˇef»ghi
 5692
 5693           j«ˇk»
 5694           n«ˇlm»o
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"abc
 5704           d«ˇef»ghi
 5705
 5706           j«ˇk»
 5707           n«ˇlm»o
 5708           "#
 5709    ));
 5710}
 5711
 5712#[gpui::test]
 5713async fn test_select_next(cx: &mut TestAppContext) {
 5714    init_test(cx, |_| {});
 5715
 5716    let mut cx = EditorTestContext::new(cx).await;
 5717    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5718
 5719    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5720        .unwrap();
 5721    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5722
 5723    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5724        .unwrap();
 5725    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5726
 5727    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5728    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5729
 5730    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5731    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5732
 5733    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5734        .unwrap();
 5735    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5736
 5737    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5738        .unwrap();
 5739    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5740}
 5741
 5742#[gpui::test]
 5743async fn test_select_all_matches(cx: &mut TestAppContext) {
 5744    init_test(cx, |_| {});
 5745
 5746    let mut cx = EditorTestContext::new(cx).await;
 5747
 5748    // Test caret-only selections
 5749    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5750    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5751        .unwrap();
 5752    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5753
 5754    // Test left-to-right selections
 5755    cx.set_state("abc\n«abcˇ»\nabc");
 5756    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5757        .unwrap();
 5758    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5759
 5760    // Test right-to-left selections
 5761    cx.set_state("abc\n«ˇabc»\nabc");
 5762    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5763        .unwrap();
 5764    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5765
 5766    // Test selecting whitespace with caret selection
 5767    cx.set_state("abc\nˇ   abc\nabc");
 5768    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5769        .unwrap();
 5770    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5771
 5772    // Test selecting whitespace with left-to-right selection
 5773    cx.set_state("abc\n«ˇ  »abc\nabc");
 5774    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5775        .unwrap();
 5776    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5777
 5778    // Test no matches with right-to-left selection
 5779    cx.set_state("abc\n«  ˇ»abc\nabc");
 5780    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5781        .unwrap();
 5782    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5783}
 5784
 5785#[gpui::test]
 5786async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5787    init_test(cx, |_| {});
 5788
 5789    let mut cx = EditorTestContext::new(cx).await;
 5790    cx.set_state(
 5791        r#"let foo = 2;
 5792lˇet foo = 2;
 5793let fooˇ = 2;
 5794let foo = 2;
 5795let foo = ˇ2;"#,
 5796    );
 5797
 5798    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5799        .unwrap();
 5800    cx.assert_editor_state(
 5801        r#"let foo = 2;
 5802«letˇ» foo = 2;
 5803let «fooˇ» = 2;
 5804let foo = 2;
 5805let foo = «2ˇ»;"#,
 5806    );
 5807
 5808    // noop for multiple selections with different contents
 5809    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5810        .unwrap();
 5811    cx.assert_editor_state(
 5812        r#"let foo = 2;
 5813«letˇ» foo = 2;
 5814let «fooˇ» = 2;
 5815let foo = 2;
 5816let foo = «2ˇ»;"#,
 5817    );
 5818}
 5819
 5820#[gpui::test]
 5821async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5822    init_test(cx, |_| {});
 5823
 5824    let mut cx =
 5825        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5826
 5827    cx.assert_editor_state(indoc! {"
 5828        ˇbbb
 5829        ccc
 5830
 5831        bbb
 5832        ccc
 5833        "});
 5834    cx.dispatch_action(SelectPrevious::default());
 5835    cx.assert_editor_state(indoc! {"
 5836                «bbbˇ»
 5837                ccc
 5838
 5839                bbb
 5840                ccc
 5841                "});
 5842    cx.dispatch_action(SelectPrevious::default());
 5843    cx.assert_editor_state(indoc! {"
 5844                «bbbˇ»
 5845                ccc
 5846
 5847                «bbbˇ»
 5848                ccc
 5849                "});
 5850}
 5851
 5852#[gpui::test]
 5853async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5854    init_test(cx, |_| {});
 5855
 5856    let mut cx = EditorTestContext::new(cx).await;
 5857    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5858
 5859    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5860        .unwrap();
 5861    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5862
 5863    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5866
 5867    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5868    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5869
 5870    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5871    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5872
 5873    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5874        .unwrap();
 5875    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5876
 5877    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5878        .unwrap();
 5879    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5880
 5881    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5882        .unwrap();
 5883    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5884}
 5885
 5886#[gpui::test]
 5887async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5888    init_test(cx, |_| {});
 5889
 5890    let mut cx = EditorTestContext::new(cx).await;
 5891    cx.set_state("");
 5892
 5893    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5894        .unwrap();
 5895    cx.assert_editor_state("«aˇ»");
 5896    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5897        .unwrap();
 5898    cx.assert_editor_state("«aˇ»");
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let mut cx = EditorTestContext::new(cx).await;
 5906    cx.set_state(
 5907        r#"let foo = 2;
 5908lˇet foo = 2;
 5909let fooˇ = 2;
 5910let foo = 2;
 5911let foo = ˇ2;"#,
 5912    );
 5913
 5914    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5915        .unwrap();
 5916    cx.assert_editor_state(
 5917        r#"let foo = 2;
 5918«letˇ» foo = 2;
 5919let «fooˇ» = 2;
 5920let foo = 2;
 5921let foo = «2ˇ»;"#,
 5922    );
 5923
 5924    // noop for multiple selections with different contents
 5925    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5926        .unwrap();
 5927    cx.assert_editor_state(
 5928        r#"let foo = 2;
 5929«letˇ» foo = 2;
 5930let «fooˇ» = 2;
 5931let foo = 2;
 5932let foo = «2ˇ»;"#,
 5933    );
 5934}
 5935
 5936#[gpui::test]
 5937async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5938    init_test(cx, |_| {});
 5939
 5940    let mut cx = EditorTestContext::new(cx).await;
 5941    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5942
 5943    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5944        .unwrap();
 5945    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5946
 5947    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5948        .unwrap();
 5949    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5950
 5951    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5952    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5953
 5954    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5955    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5956
 5957    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5958        .unwrap();
 5959    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5960
 5961    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5962        .unwrap();
 5963    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5964}
 5965
 5966#[gpui::test]
 5967async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5968    init_test(cx, |_| {});
 5969
 5970    let language = Arc::new(Language::new(
 5971        LanguageConfig::default(),
 5972        Some(tree_sitter_rust::LANGUAGE.into()),
 5973    ));
 5974
 5975    let text = r#"
 5976        use mod1::mod2::{mod3, mod4};
 5977
 5978        fn fn_1(param1: bool, param2: &str) {
 5979            let var1 = "text";
 5980        }
 5981    "#
 5982    .unindent();
 5983
 5984    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5985    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5986    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5987
 5988    editor
 5989        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5990        .await;
 5991
 5992    editor.update_in(cx, |editor, window, cx| {
 5993        editor.change_selections(None, window, cx, |s| {
 5994            s.select_display_ranges([
 5995                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5996                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5997                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5998            ]);
 5999        });
 6000        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6001    });
 6002    editor.update(cx, |editor, cx| {
 6003        assert_text_with_selections(
 6004            editor,
 6005            indoc! {r#"
 6006                use mod1::mod2::{mod3, «mod4ˇ»};
 6007
 6008                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6009                    let var1 = "«ˇtext»";
 6010                }
 6011            "#},
 6012            cx,
 6013        );
 6014    });
 6015
 6016    editor.update_in(cx, |editor, window, cx| {
 6017        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6018    });
 6019    editor.update(cx, |editor, cx| {
 6020        assert_text_with_selections(
 6021            editor,
 6022            indoc! {r#"
 6023                use mod1::mod2::«{mod3, mod4}ˇ»;
 6024
 6025                «ˇfn fn_1(param1: bool, param2: &str) {
 6026                    let var1 = "text";
 6027 6028            "#},
 6029            cx,
 6030        );
 6031    });
 6032
 6033    editor.update_in(cx, |editor, window, cx| {
 6034        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6035    });
 6036    assert_eq!(
 6037        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6038        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6039    );
 6040
 6041    // Trying to expand the selected syntax node one more time has no effect.
 6042    editor.update_in(cx, |editor, window, cx| {
 6043        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6044    });
 6045    assert_eq!(
 6046        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6047        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6048    );
 6049
 6050    editor.update_in(cx, |editor, window, cx| {
 6051        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6052    });
 6053    editor.update(cx, |editor, cx| {
 6054        assert_text_with_selections(
 6055            editor,
 6056            indoc! {r#"
 6057                use mod1::mod2::«{mod3, mod4}ˇ»;
 6058
 6059                «ˇfn fn_1(param1: bool, param2: &str) {
 6060                    let var1 = "text";
 6061 6062            "#},
 6063            cx,
 6064        );
 6065    });
 6066
 6067    editor.update_in(cx, |editor, window, cx| {
 6068        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6069    });
 6070    editor.update(cx, |editor, cx| {
 6071        assert_text_with_selections(
 6072            editor,
 6073            indoc! {r#"
 6074                use mod1::mod2::{mod3, «mod4ˇ»};
 6075
 6076                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6077                    let var1 = "«ˇtext»";
 6078                }
 6079            "#},
 6080            cx,
 6081        );
 6082    });
 6083
 6084    editor.update_in(cx, |editor, window, cx| {
 6085        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6086    });
 6087    editor.update(cx, |editor, cx| {
 6088        assert_text_with_selections(
 6089            editor,
 6090            indoc! {r#"
 6091                use mod1::mod2::{mod3, mo«ˇ»d4};
 6092
 6093                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6094                    let var1 = "te«ˇ»xt";
 6095                }
 6096            "#},
 6097            cx,
 6098        );
 6099    });
 6100
 6101    // Trying to shrink the selected syntax node one more time has no effect.
 6102    editor.update_in(cx, |editor, window, cx| {
 6103        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6104    });
 6105    editor.update_in(cx, |editor, _, cx| {
 6106        assert_text_with_selections(
 6107            editor,
 6108            indoc! {r#"
 6109                use mod1::mod2::{mod3, mo«ˇ»d4};
 6110
 6111                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6112                    let var1 = "te«ˇ»xt";
 6113                }
 6114            "#},
 6115            cx,
 6116        );
 6117    });
 6118
 6119    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6120    // a fold.
 6121    editor.update_in(cx, |editor, window, cx| {
 6122        editor.fold_creases(
 6123            vec![
 6124                Crease::simple(
 6125                    Point::new(0, 21)..Point::new(0, 24),
 6126                    FoldPlaceholder::test(),
 6127                ),
 6128                Crease::simple(
 6129                    Point::new(3, 20)..Point::new(3, 22),
 6130                    FoldPlaceholder::test(),
 6131                ),
 6132            ],
 6133            true,
 6134            window,
 6135            cx,
 6136        );
 6137        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6138    });
 6139    editor.update(cx, |editor, cx| {
 6140        assert_text_with_selections(
 6141            editor,
 6142            indoc! {r#"
 6143                use mod1::mod2::«{mod3, mod4}ˇ»;
 6144
 6145                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6146                    «ˇlet var1 = "text";»
 6147                }
 6148            "#},
 6149            cx,
 6150        );
 6151    });
 6152}
 6153
 6154#[gpui::test]
 6155async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6156    init_test(cx, |_| {});
 6157
 6158    let base_text = r#"
 6159        impl A {
 6160            // this is an uncommitted comment
 6161
 6162            fn b() {
 6163                c();
 6164            }
 6165
 6166            // this is another uncommitted comment
 6167
 6168            fn d() {
 6169                // e
 6170                // f
 6171            }
 6172        }
 6173
 6174        fn g() {
 6175            // h
 6176        }
 6177    "#
 6178    .unindent();
 6179
 6180    let text = r#"
 6181        ˇimpl A {
 6182
 6183            fn b() {
 6184                c();
 6185            }
 6186
 6187            fn d() {
 6188                // e
 6189                // f
 6190            }
 6191        }
 6192
 6193        fn g() {
 6194            // h
 6195        }
 6196    "#
 6197    .unindent();
 6198
 6199    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6200    cx.set_state(&text);
 6201    cx.set_head_text(&base_text);
 6202    cx.update_editor(|editor, window, cx| {
 6203        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6204    });
 6205
 6206    cx.assert_state_with_diff(
 6207        "
 6208        ˇimpl A {
 6209      -     // this is an uncommitted comment
 6210
 6211            fn b() {
 6212                c();
 6213            }
 6214
 6215      -     // this is another uncommitted comment
 6216      -
 6217            fn d() {
 6218                // e
 6219                // f
 6220            }
 6221        }
 6222
 6223        fn g() {
 6224            // h
 6225        }
 6226    "
 6227        .unindent(),
 6228    );
 6229
 6230    let expected_display_text = "
 6231        impl A {
 6232            // this is an uncommitted comment
 6233
 6234            fn b() {
 6235 6236            }
 6237
 6238            // this is another uncommitted comment
 6239
 6240            fn d() {
 6241 6242            }
 6243        }
 6244
 6245        fn g() {
 6246 6247        }
 6248        "
 6249    .unindent();
 6250
 6251    cx.update_editor(|editor, window, cx| {
 6252        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6253        assert_eq!(editor.display_text(cx), expected_display_text);
 6254    });
 6255}
 6256
 6257#[gpui::test]
 6258async fn test_autoindent(cx: &mut TestAppContext) {
 6259    init_test(cx, |_| {});
 6260
 6261    let language = Arc::new(
 6262        Language::new(
 6263            LanguageConfig {
 6264                brackets: BracketPairConfig {
 6265                    pairs: vec![
 6266                        BracketPair {
 6267                            start: "{".to_string(),
 6268                            end: "}".to_string(),
 6269                            close: false,
 6270                            surround: false,
 6271                            newline: true,
 6272                        },
 6273                        BracketPair {
 6274                            start: "(".to_string(),
 6275                            end: ")".to_string(),
 6276                            close: false,
 6277                            surround: false,
 6278                            newline: true,
 6279                        },
 6280                    ],
 6281                    ..Default::default()
 6282                },
 6283                ..Default::default()
 6284            },
 6285            Some(tree_sitter_rust::LANGUAGE.into()),
 6286        )
 6287        .with_indents_query(
 6288            r#"
 6289                (_ "(" ")" @end) @indent
 6290                (_ "{" "}" @end) @indent
 6291            "#,
 6292        )
 6293        .unwrap(),
 6294    );
 6295
 6296    let text = "fn a() {}";
 6297
 6298    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6299    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6300    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6301    editor
 6302        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6303        .await;
 6304
 6305    editor.update_in(cx, |editor, window, cx| {
 6306        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6307        editor.newline(&Newline, window, cx);
 6308        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6309        assert_eq!(
 6310            editor.selections.ranges(cx),
 6311            &[
 6312                Point::new(1, 4)..Point::new(1, 4),
 6313                Point::new(3, 4)..Point::new(3, 4),
 6314                Point::new(5, 0)..Point::new(5, 0)
 6315            ]
 6316        );
 6317    });
 6318}
 6319
 6320#[gpui::test]
 6321async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6322    init_test(cx, |_| {});
 6323
 6324    {
 6325        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6326        cx.set_state(indoc! {"
 6327            impl A {
 6328
 6329                fn b() {}
 6330
 6331            «fn c() {
 6332
 6333            }ˇ»
 6334            }
 6335        "});
 6336
 6337        cx.update_editor(|editor, window, cx| {
 6338            editor.autoindent(&Default::default(), window, cx);
 6339        });
 6340
 6341        cx.assert_editor_state(indoc! {"
 6342            impl A {
 6343
 6344                fn b() {}
 6345
 6346                «fn c() {
 6347
 6348                }ˇ»
 6349            }
 6350        "});
 6351    }
 6352
 6353    {
 6354        let mut cx = EditorTestContext::new_multibuffer(
 6355            cx,
 6356            [indoc! { "
 6357                impl A {
 6358                «
 6359                // a
 6360                fn b(){}
 6361                »
 6362                «
 6363                    }
 6364                    fn c(){}
 6365                »
 6366            "}],
 6367        );
 6368
 6369        let buffer = cx.update_editor(|editor, _, cx| {
 6370            let buffer = editor.buffer().update(cx, |buffer, _| {
 6371                buffer.all_buffers().iter().next().unwrap().clone()
 6372            });
 6373            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6374            buffer
 6375        });
 6376
 6377        cx.run_until_parked();
 6378        cx.update_editor(|editor, window, cx| {
 6379            editor.select_all(&Default::default(), window, cx);
 6380            editor.autoindent(&Default::default(), window, cx)
 6381        });
 6382        cx.run_until_parked();
 6383
 6384        cx.update(|_, cx| {
 6385            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_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10240    init_test(cx, |_| {});
10241
10242    let mut cx = EditorLspTestContext::new_rust(
10243        lsp::ServerCapabilities {
10244            completion_provider: Some(lsp::CompletionOptions {
10245                trigger_characters: Some(vec![".".to_string()]),
10246                resolve_provider: Some(true),
10247                ..Default::default()
10248            }),
10249            ..Default::default()
10250        },
10251        cx,
10252    )
10253    .await;
10254
10255    cx.set_state("fn main() { let a = 2ˇ; }");
10256    cx.simulate_keystroke(".");
10257    let completion_item = lsp::CompletionItem {
10258        label: "Some".into(),
10259        kind: Some(lsp::CompletionItemKind::SNIPPET),
10260        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10261        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10262            kind: lsp::MarkupKind::Markdown,
10263            value: "```rust\nSome(2)\n```".to_string(),
10264        })),
10265        deprecated: Some(false),
10266        sort_text: Some("Some".to_string()),
10267        filter_text: Some("Some".to_string()),
10268        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10269        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10270            range: lsp::Range {
10271                start: lsp::Position {
10272                    line: 0,
10273                    character: 22,
10274                },
10275                end: lsp::Position {
10276                    line: 0,
10277                    character: 22,
10278                },
10279            },
10280            new_text: "Some(2)".to_string(),
10281        })),
10282        additional_text_edits: Some(vec![lsp::TextEdit {
10283            range: lsp::Range {
10284                start: lsp::Position {
10285                    line: 0,
10286                    character: 20,
10287                },
10288                end: lsp::Position {
10289                    line: 0,
10290                    character: 22,
10291                },
10292            },
10293            new_text: "".to_string(),
10294        }]),
10295        ..Default::default()
10296    };
10297
10298    let closure_completion_item = completion_item.clone();
10299    let counter = Arc::new(AtomicUsize::new(0));
10300    let counter_clone = counter.clone();
10301    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10302        let task_completion_item = closure_completion_item.clone();
10303        counter_clone.fetch_add(1, atomic::Ordering::Release);
10304        async move {
10305            Ok(Some(lsp::CompletionResponse::Array(vec![
10306                task_completion_item,
10307            ])))
10308        }
10309    });
10310
10311    cx.condition(|editor, _| editor.context_menu_visible())
10312        .await;
10313    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10314    assert!(request.next().await.is_some());
10315    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10316
10317    cx.simulate_keystrokes("S o m");
10318    cx.condition(|editor, _| editor.context_menu_visible())
10319        .await;
10320    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10321    assert!(request.next().await.is_some());
10322    assert!(request.next().await.is_some());
10323    assert!(request.next().await.is_some());
10324    request.close();
10325    assert!(request.next().await.is_none());
10326    assert_eq!(
10327        counter.load(atomic::Ordering::Acquire),
10328        4,
10329        "With the completions menu open, only one LSP request should happen per input"
10330    );
10331}
10332
10333#[gpui::test]
10334async fn test_toggle_comment(cx: &mut TestAppContext) {
10335    init_test(cx, |_| {});
10336    let mut cx = EditorTestContext::new(cx).await;
10337    let language = Arc::new(Language::new(
10338        LanguageConfig {
10339            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10340            ..Default::default()
10341        },
10342        Some(tree_sitter_rust::LANGUAGE.into()),
10343    ));
10344    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10345
10346    // If multiple selections intersect a line, the line is only toggled once.
10347    cx.set_state(indoc! {"
10348        fn a() {
10349            «//b();
10350            ˇ»// «c();
10351            //ˇ»  d();
10352        }
10353    "});
10354
10355    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10356
10357    cx.assert_editor_state(indoc! {"
10358        fn a() {
10359            «b();
10360            c();
10361            ˇ» d();
10362        }
10363    "});
10364
10365    // The comment prefix is inserted at the same column for every line in a
10366    // selection.
10367    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10368
10369    cx.assert_editor_state(indoc! {"
10370        fn a() {
10371            // «b();
10372            // c();
10373            ˇ»//  d();
10374        }
10375    "});
10376
10377    // If a selection ends at the beginning of a line, that line is not toggled.
10378    cx.set_selections_state(indoc! {"
10379        fn a() {
10380            // b();
10381            «// c();
10382        ˇ»    //  d();
10383        }
10384    "});
10385
10386    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10387
10388    cx.assert_editor_state(indoc! {"
10389        fn a() {
10390            // b();
10391            «c();
10392        ˇ»    //  d();
10393        }
10394    "});
10395
10396    // If a selection span a single line and is empty, the line is toggled.
10397    cx.set_state(indoc! {"
10398        fn a() {
10399            a();
10400            b();
10401        ˇ
10402        }
10403    "});
10404
10405    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10406
10407    cx.assert_editor_state(indoc! {"
10408        fn a() {
10409            a();
10410            b();
10411        //•ˇ
10412        }
10413    "});
10414
10415    // If a selection span multiple lines, empty lines are not toggled.
10416    cx.set_state(indoc! {"
10417        fn a() {
10418            «a();
10419
10420            c();ˇ»
10421        }
10422    "});
10423
10424    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10425
10426    cx.assert_editor_state(indoc! {"
10427        fn a() {
10428            // «a();
10429
10430            // c();ˇ»
10431        }
10432    "});
10433
10434    // If a selection includes multiple comment prefixes, all lines are uncommented.
10435    cx.set_state(indoc! {"
10436        fn a() {
10437            «// a();
10438            /// b();
10439            //! c();ˇ»
10440        }
10441    "});
10442
10443    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10444
10445    cx.assert_editor_state(indoc! {"
10446        fn a() {
10447            «a();
10448            b();
10449            c();ˇ»
10450        }
10451    "});
10452}
10453
10454#[gpui::test]
10455async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10456    init_test(cx, |_| {});
10457    let mut cx = EditorTestContext::new(cx).await;
10458    let language = Arc::new(Language::new(
10459        LanguageConfig {
10460            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10461            ..Default::default()
10462        },
10463        Some(tree_sitter_rust::LANGUAGE.into()),
10464    ));
10465    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10466
10467    let toggle_comments = &ToggleComments {
10468        advance_downwards: false,
10469        ignore_indent: true,
10470    };
10471
10472    // If multiple selections intersect a line, the line is only toggled once.
10473    cx.set_state(indoc! {"
10474        fn a() {
10475        //    «b();
10476        //    c();
10477        //    ˇ» d();
10478        }
10479    "});
10480
10481    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10482
10483    cx.assert_editor_state(indoc! {"
10484        fn a() {
10485            «b();
10486            c();
10487            ˇ» d();
10488        }
10489    "});
10490
10491    // The comment prefix is inserted at the beginning of each line
10492    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10493
10494    cx.assert_editor_state(indoc! {"
10495        fn a() {
10496        //    «b();
10497        //    c();
10498        //    ˇ» d();
10499        }
10500    "});
10501
10502    // If a selection ends at the beginning of a line, that line is not toggled.
10503    cx.set_selections_state(indoc! {"
10504        fn a() {
10505        //    b();
10506        //    «c();
10507        ˇ»//     d();
10508        }
10509    "});
10510
10511    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10512
10513    cx.assert_editor_state(indoc! {"
10514        fn a() {
10515        //    b();
10516            «c();
10517        ˇ»//     d();
10518        }
10519    "});
10520
10521    // If a selection span a single line and is empty, the line is toggled.
10522    cx.set_state(indoc! {"
10523        fn a() {
10524            a();
10525            b();
10526        ˇ
10527        }
10528    "});
10529
10530    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10531
10532    cx.assert_editor_state(indoc! {"
10533        fn a() {
10534            a();
10535            b();
10536        //ˇ
10537        }
10538    "});
10539
10540    // If a selection span multiple lines, empty lines are not toggled.
10541    cx.set_state(indoc! {"
10542        fn a() {
10543            «a();
10544
10545            c();ˇ»
10546        }
10547    "});
10548
10549    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10550
10551    cx.assert_editor_state(indoc! {"
10552        fn a() {
10553        //    «a();
10554
10555        //    c();ˇ»
10556        }
10557    "});
10558
10559    // If a selection includes multiple comment prefixes, all lines are uncommented.
10560    cx.set_state(indoc! {"
10561        fn a() {
10562        //    «a();
10563        ///    b();
10564        //!    c();ˇ»
10565        }
10566    "});
10567
10568    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10569
10570    cx.assert_editor_state(indoc! {"
10571        fn a() {
10572            «a();
10573            b();
10574            c();ˇ»
10575        }
10576    "});
10577}
10578
10579#[gpui::test]
10580async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10581    init_test(cx, |_| {});
10582
10583    let language = Arc::new(Language::new(
10584        LanguageConfig {
10585            line_comments: vec!["// ".into()],
10586            ..Default::default()
10587        },
10588        Some(tree_sitter_rust::LANGUAGE.into()),
10589    ));
10590
10591    let mut cx = EditorTestContext::new(cx).await;
10592
10593    cx.language_registry().add(language.clone());
10594    cx.update_buffer(|buffer, cx| {
10595        buffer.set_language(Some(language), cx);
10596    });
10597
10598    let toggle_comments = &ToggleComments {
10599        advance_downwards: true,
10600        ignore_indent: false,
10601    };
10602
10603    // Single cursor on one line -> advance
10604    // Cursor moves horizontally 3 characters as well on non-blank line
10605    cx.set_state(indoc!(
10606        "fn a() {
10607             ˇdog();
10608             cat();
10609        }"
10610    ));
10611    cx.update_editor(|editor, window, cx| {
10612        editor.toggle_comments(toggle_comments, window, cx);
10613    });
10614    cx.assert_editor_state(indoc!(
10615        "fn a() {
10616             // dog();
10617             catˇ();
10618        }"
10619    ));
10620
10621    // Single selection on one line -> don't advance
10622    cx.set_state(indoc!(
10623        "fn a() {
10624             «dog()ˇ»;
10625             cat();
10626        }"
10627    ));
10628    cx.update_editor(|editor, window, cx| {
10629        editor.toggle_comments(toggle_comments, window, cx);
10630    });
10631    cx.assert_editor_state(indoc!(
10632        "fn a() {
10633             // «dog()ˇ»;
10634             cat();
10635        }"
10636    ));
10637
10638    // Multiple cursors on one line -> advance
10639    cx.set_state(indoc!(
10640        "fn a() {
10641             ˇdˇog();
10642             cat();
10643        }"
10644    ));
10645    cx.update_editor(|editor, window, cx| {
10646        editor.toggle_comments(toggle_comments, window, cx);
10647    });
10648    cx.assert_editor_state(indoc!(
10649        "fn a() {
10650             // dog();
10651             catˇ(ˇ);
10652        }"
10653    ));
10654
10655    // Multiple cursors on one line, with selection -> don't advance
10656    cx.set_state(indoc!(
10657        "fn a() {
10658             ˇdˇog«()ˇ»;
10659             cat();
10660        }"
10661    ));
10662    cx.update_editor(|editor, window, cx| {
10663        editor.toggle_comments(toggle_comments, window, cx);
10664    });
10665    cx.assert_editor_state(indoc!(
10666        "fn a() {
10667             // ˇdˇog«()ˇ»;
10668             cat();
10669        }"
10670    ));
10671
10672    // Single cursor on one line -> advance
10673    // Cursor moves to column 0 on blank line
10674    cx.set_state(indoc!(
10675        "fn a() {
10676             ˇdog();
10677
10678             cat();
10679        }"
10680    ));
10681    cx.update_editor(|editor, window, cx| {
10682        editor.toggle_comments(toggle_comments, window, cx);
10683    });
10684    cx.assert_editor_state(indoc!(
10685        "fn a() {
10686             // dog();
10687        ˇ
10688             cat();
10689        }"
10690    ));
10691
10692    // Single cursor on one line -> advance
10693    // Cursor starts and ends at column 0
10694    cx.set_state(indoc!(
10695        "fn a() {
10696         ˇ    dog();
10697             cat();
10698        }"
10699    ));
10700    cx.update_editor(|editor, window, cx| {
10701        editor.toggle_comments(toggle_comments, window, cx);
10702    });
10703    cx.assert_editor_state(indoc!(
10704        "fn a() {
10705             // dog();
10706         ˇ    cat();
10707        }"
10708    ));
10709}
10710
10711#[gpui::test]
10712async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10713    init_test(cx, |_| {});
10714
10715    let mut cx = EditorTestContext::new(cx).await;
10716
10717    let html_language = Arc::new(
10718        Language::new(
10719            LanguageConfig {
10720                name: "HTML".into(),
10721                block_comment: Some(("<!-- ".into(), " -->".into())),
10722                ..Default::default()
10723            },
10724            Some(tree_sitter_html::LANGUAGE.into()),
10725        )
10726        .with_injection_query(
10727            r#"
10728            (script_element
10729                (raw_text) @injection.content
10730                (#set! injection.language "javascript"))
10731            "#,
10732        )
10733        .unwrap(),
10734    );
10735
10736    let javascript_language = Arc::new(Language::new(
10737        LanguageConfig {
10738            name: "JavaScript".into(),
10739            line_comments: vec!["// ".into()],
10740            ..Default::default()
10741        },
10742        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10743    ));
10744
10745    cx.language_registry().add(html_language.clone());
10746    cx.language_registry().add(javascript_language.clone());
10747    cx.update_buffer(|buffer, cx| {
10748        buffer.set_language(Some(html_language), cx);
10749    });
10750
10751    // Toggle comments for empty selections
10752    cx.set_state(
10753        &r#"
10754            <p>A</p>ˇ
10755            <p>B</p>ˇ
10756            <p>C</p>ˇ
10757        "#
10758        .unindent(),
10759    );
10760    cx.update_editor(|editor, window, cx| {
10761        editor.toggle_comments(&ToggleComments::default(), window, cx)
10762    });
10763    cx.assert_editor_state(
10764        &r#"
10765            <!-- <p>A</p>ˇ -->
10766            <!-- <p>B</p>ˇ -->
10767            <!-- <p>C</p>ˇ -->
10768        "#
10769        .unindent(),
10770    );
10771    cx.update_editor(|editor, window, cx| {
10772        editor.toggle_comments(&ToggleComments::default(), window, cx)
10773    });
10774    cx.assert_editor_state(
10775        &r#"
10776            <p>A</p>ˇ
10777            <p>B</p>ˇ
10778            <p>C</p>ˇ
10779        "#
10780        .unindent(),
10781    );
10782
10783    // Toggle comments for mixture of empty and non-empty selections, where
10784    // multiple selections occupy a given line.
10785    cx.set_state(
10786        &r#"
10787            <p>A«</p>
10788            <p>ˇ»B</p>ˇ
10789            <p>C«</p>
10790            <p>ˇ»D</p>ˇ
10791        "#
10792        .unindent(),
10793    );
10794
10795    cx.update_editor(|editor, window, cx| {
10796        editor.toggle_comments(&ToggleComments::default(), window, cx)
10797    });
10798    cx.assert_editor_state(
10799        &r#"
10800            <!-- <p>A«</p>
10801            <p>ˇ»B</p>ˇ -->
10802            <!-- <p>C«</p>
10803            <p>ˇ»D</p>ˇ -->
10804        "#
10805        .unindent(),
10806    );
10807    cx.update_editor(|editor, window, cx| {
10808        editor.toggle_comments(&ToggleComments::default(), window, cx)
10809    });
10810    cx.assert_editor_state(
10811        &r#"
10812            <p>A«</p>
10813            <p>ˇ»B</p>ˇ
10814            <p>C«</p>
10815            <p>ˇ»D</p>ˇ
10816        "#
10817        .unindent(),
10818    );
10819
10820    // Toggle comments when different languages are active for different
10821    // selections.
10822    cx.set_state(
10823        &r#"
10824            ˇ<script>
10825                ˇvar x = new Y();
10826            ˇ</script>
10827        "#
10828        .unindent(),
10829    );
10830    cx.executor().run_until_parked();
10831    cx.update_editor(|editor, window, cx| {
10832        editor.toggle_comments(&ToggleComments::default(), window, cx)
10833    });
10834    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10835    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10836    cx.assert_editor_state(
10837        &r#"
10838            <!-- ˇ<script> -->
10839                // ˇvar x = new Y();
10840            <!-- ˇ</script> -->
10841        "#
10842        .unindent(),
10843    );
10844}
10845
10846#[gpui::test]
10847fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10848    init_test(cx, |_| {});
10849
10850    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10851    let multibuffer = cx.new(|cx| {
10852        let mut multibuffer = MultiBuffer::new(ReadWrite);
10853        multibuffer.push_excerpts(
10854            buffer.clone(),
10855            [
10856                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
10857                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
10858            ],
10859            cx,
10860        );
10861        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10862        multibuffer
10863    });
10864
10865    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10866    editor.update_in(cx, |editor, window, cx| {
10867        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10868        editor.change_selections(None, window, cx, |s| {
10869            s.select_ranges([
10870                Point::new(0, 0)..Point::new(0, 0),
10871                Point::new(1, 0)..Point::new(1, 0),
10872            ])
10873        });
10874
10875        editor.handle_input("X", window, cx);
10876        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10877        assert_eq!(
10878            editor.selections.ranges(cx),
10879            [
10880                Point::new(0, 1)..Point::new(0, 1),
10881                Point::new(1, 1)..Point::new(1, 1),
10882            ]
10883        );
10884
10885        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10886        editor.change_selections(None, window, cx, |s| {
10887            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10888        });
10889        editor.backspace(&Default::default(), window, cx);
10890        assert_eq!(editor.text(cx), "Xa\nbbb");
10891        assert_eq!(
10892            editor.selections.ranges(cx),
10893            [Point::new(1, 0)..Point::new(1, 0)]
10894        );
10895
10896        editor.change_selections(None, window, cx, |s| {
10897            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10898        });
10899        editor.backspace(&Default::default(), window, cx);
10900        assert_eq!(editor.text(cx), "X\nbb");
10901        assert_eq!(
10902            editor.selections.ranges(cx),
10903            [Point::new(0, 1)..Point::new(0, 1)]
10904        );
10905    });
10906}
10907
10908#[gpui::test]
10909fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10910    init_test(cx, |_| {});
10911
10912    let markers = vec![('[', ']').into(), ('(', ')').into()];
10913    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10914        indoc! {"
10915            [aaaa
10916            (bbbb]
10917            cccc)",
10918        },
10919        markers.clone(),
10920    );
10921    let excerpt_ranges = markers.into_iter().map(|marker| {
10922        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10923        ExcerptRange::new(context.clone())
10924    });
10925    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10926    let multibuffer = cx.new(|cx| {
10927        let mut multibuffer = MultiBuffer::new(ReadWrite);
10928        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10929        multibuffer
10930    });
10931
10932    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10933    editor.update_in(cx, |editor, window, cx| {
10934        let (expected_text, selection_ranges) = marked_text_ranges(
10935            indoc! {"
10936                aaaa
10937                bˇbbb
10938                bˇbbˇb
10939                cccc"
10940            },
10941            true,
10942        );
10943        assert_eq!(editor.text(cx), expected_text);
10944        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10945
10946        editor.handle_input("X", window, cx);
10947
10948        let (expected_text, expected_selections) = marked_text_ranges(
10949            indoc! {"
10950                aaaa
10951                bXˇbbXb
10952                bXˇbbXˇb
10953                cccc"
10954            },
10955            false,
10956        );
10957        assert_eq!(editor.text(cx), expected_text);
10958        assert_eq!(editor.selections.ranges(cx), expected_selections);
10959
10960        editor.newline(&Newline, window, cx);
10961        let (expected_text, expected_selections) = marked_text_ranges(
10962            indoc! {"
10963                aaaa
10964                bX
10965                ˇbbX
10966                b
10967                bX
10968                ˇbbX
10969                ˇb
10970                cccc"
10971            },
10972            false,
10973        );
10974        assert_eq!(editor.text(cx), expected_text);
10975        assert_eq!(editor.selections.ranges(cx), expected_selections);
10976    });
10977}
10978
10979#[gpui::test]
10980fn test_refresh_selections(cx: &mut TestAppContext) {
10981    init_test(cx, |_| {});
10982
10983    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10984    let mut excerpt1_id = None;
10985    let multibuffer = cx.new(|cx| {
10986        let mut multibuffer = MultiBuffer::new(ReadWrite);
10987        excerpt1_id = multibuffer
10988            .push_excerpts(
10989                buffer.clone(),
10990                [
10991                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
10992                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
10993                ],
10994                cx,
10995            )
10996            .into_iter()
10997            .next();
10998        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10999        multibuffer
11000    });
11001
11002    let editor = cx.add_window(|window, cx| {
11003        let mut editor = build_editor(multibuffer.clone(), window, cx);
11004        let snapshot = editor.snapshot(window, cx);
11005        editor.change_selections(None, window, cx, |s| {
11006            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11007        });
11008        editor.begin_selection(
11009            Point::new(2, 1).to_display_point(&snapshot),
11010            true,
11011            1,
11012            window,
11013            cx,
11014        );
11015        assert_eq!(
11016            editor.selections.ranges(cx),
11017            [
11018                Point::new(1, 3)..Point::new(1, 3),
11019                Point::new(2, 1)..Point::new(2, 1),
11020            ]
11021        );
11022        editor
11023    });
11024
11025    // Refreshing selections is a no-op when excerpts haven't changed.
11026    _ = editor.update(cx, |editor, window, cx| {
11027        editor.change_selections(None, window, cx, |s| s.refresh());
11028        assert_eq!(
11029            editor.selections.ranges(cx),
11030            [
11031                Point::new(1, 3)..Point::new(1, 3),
11032                Point::new(2, 1)..Point::new(2, 1),
11033            ]
11034        );
11035    });
11036
11037    multibuffer.update(cx, |multibuffer, cx| {
11038        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11039    });
11040    _ = editor.update(cx, |editor, window, cx| {
11041        // Removing an excerpt causes the first selection to become degenerate.
11042        assert_eq!(
11043            editor.selections.ranges(cx),
11044            [
11045                Point::new(0, 0)..Point::new(0, 0),
11046                Point::new(0, 1)..Point::new(0, 1)
11047            ]
11048        );
11049
11050        // Refreshing selections will relocate the first selection to the original buffer
11051        // location.
11052        editor.change_selections(None, window, cx, |s| s.refresh());
11053        assert_eq!(
11054            editor.selections.ranges(cx),
11055            [
11056                Point::new(0, 1)..Point::new(0, 1),
11057                Point::new(0, 3)..Point::new(0, 3)
11058            ]
11059        );
11060        assert!(editor.selections.pending_anchor().is_some());
11061    });
11062}
11063
11064#[gpui::test]
11065fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11066    init_test(cx, |_| {});
11067
11068    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11069    let mut excerpt1_id = None;
11070    let multibuffer = cx.new(|cx| {
11071        let mut multibuffer = MultiBuffer::new(ReadWrite);
11072        excerpt1_id = multibuffer
11073            .push_excerpts(
11074                buffer.clone(),
11075                [
11076                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11077                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11078                ],
11079                cx,
11080            )
11081            .into_iter()
11082            .next();
11083        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11084        multibuffer
11085    });
11086
11087    let editor = cx.add_window(|window, cx| {
11088        let mut editor = build_editor(multibuffer.clone(), window, cx);
11089        let snapshot = editor.snapshot(window, cx);
11090        editor.begin_selection(
11091            Point::new(1, 3).to_display_point(&snapshot),
11092            false,
11093            1,
11094            window,
11095            cx,
11096        );
11097        assert_eq!(
11098            editor.selections.ranges(cx),
11099            [Point::new(1, 3)..Point::new(1, 3)]
11100        );
11101        editor
11102    });
11103
11104    multibuffer.update(cx, |multibuffer, cx| {
11105        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11106    });
11107    _ = editor.update(cx, |editor, window, cx| {
11108        assert_eq!(
11109            editor.selections.ranges(cx),
11110            [Point::new(0, 0)..Point::new(0, 0)]
11111        );
11112
11113        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11114        editor.change_selections(None, window, cx, |s| s.refresh());
11115        assert_eq!(
11116            editor.selections.ranges(cx),
11117            [Point::new(0, 3)..Point::new(0, 3)]
11118        );
11119        assert!(editor.selections.pending_anchor().is_some());
11120    });
11121}
11122
11123#[gpui::test]
11124async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11125    init_test(cx, |_| {});
11126
11127    let language = Arc::new(
11128        Language::new(
11129            LanguageConfig {
11130                brackets: BracketPairConfig {
11131                    pairs: vec![
11132                        BracketPair {
11133                            start: "{".to_string(),
11134                            end: "}".to_string(),
11135                            close: true,
11136                            surround: true,
11137                            newline: true,
11138                        },
11139                        BracketPair {
11140                            start: "/* ".to_string(),
11141                            end: " */".to_string(),
11142                            close: true,
11143                            surround: true,
11144                            newline: true,
11145                        },
11146                    ],
11147                    ..Default::default()
11148                },
11149                ..Default::default()
11150            },
11151            Some(tree_sitter_rust::LANGUAGE.into()),
11152        )
11153        .with_indents_query("")
11154        .unwrap(),
11155    );
11156
11157    let text = concat!(
11158        "{   }\n",     //
11159        "  x\n",       //
11160        "  /*   */\n", //
11161        "x\n",         //
11162        "{{} }\n",     //
11163    );
11164
11165    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11166    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11167    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11168    editor
11169        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11170        .await;
11171
11172    editor.update_in(cx, |editor, window, cx| {
11173        editor.change_selections(None, window, cx, |s| {
11174            s.select_display_ranges([
11175                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11176                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11177                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11178            ])
11179        });
11180        editor.newline(&Newline, window, cx);
11181
11182        assert_eq!(
11183            editor.buffer().read(cx).read(cx).text(),
11184            concat!(
11185                "{ \n",    // Suppress rustfmt
11186                "\n",      //
11187                "}\n",     //
11188                "  x\n",   //
11189                "  /* \n", //
11190                "  \n",    //
11191                "  */\n",  //
11192                "x\n",     //
11193                "{{} \n",  //
11194                "}\n",     //
11195            )
11196        );
11197    });
11198}
11199
11200#[gpui::test]
11201fn test_highlighted_ranges(cx: &mut TestAppContext) {
11202    init_test(cx, |_| {});
11203
11204    let editor = cx.add_window(|window, cx| {
11205        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11206        build_editor(buffer.clone(), window, cx)
11207    });
11208
11209    _ = editor.update(cx, |editor, window, cx| {
11210        struct Type1;
11211        struct Type2;
11212
11213        let buffer = editor.buffer.read(cx).snapshot(cx);
11214
11215        let anchor_range =
11216            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11217
11218        editor.highlight_background::<Type1>(
11219            &[
11220                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11221                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11222                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11223                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11224            ],
11225            |_| Hsla::red(),
11226            cx,
11227        );
11228        editor.highlight_background::<Type2>(
11229            &[
11230                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11231                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11232                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11233                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11234            ],
11235            |_| Hsla::green(),
11236            cx,
11237        );
11238
11239        let snapshot = editor.snapshot(window, cx);
11240        let mut highlighted_ranges = editor.background_highlights_in_range(
11241            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11242            &snapshot,
11243            cx.theme().colors(),
11244        );
11245        // Enforce a consistent ordering based on color without relying on the ordering of the
11246        // highlight's `TypeId` which is non-executor.
11247        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11248        assert_eq!(
11249            highlighted_ranges,
11250            &[
11251                (
11252                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11253                    Hsla::red(),
11254                ),
11255                (
11256                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11257                    Hsla::red(),
11258                ),
11259                (
11260                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11261                    Hsla::green(),
11262                ),
11263                (
11264                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11265                    Hsla::green(),
11266                ),
11267            ]
11268        );
11269        assert_eq!(
11270            editor.background_highlights_in_range(
11271                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11272                &snapshot,
11273                cx.theme().colors(),
11274            ),
11275            &[(
11276                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11277                Hsla::red(),
11278            )]
11279        );
11280    });
11281}
11282
11283#[gpui::test]
11284async fn test_following(cx: &mut TestAppContext) {
11285    init_test(cx, |_| {});
11286
11287    let fs = FakeFs::new(cx.executor());
11288    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11289
11290    let buffer = project.update(cx, |project, cx| {
11291        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11292        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11293    });
11294    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11295    let follower = cx.update(|cx| {
11296        cx.open_window(
11297            WindowOptions {
11298                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11299                    gpui::Point::new(px(0.), px(0.)),
11300                    gpui::Point::new(px(10.), px(80.)),
11301                ))),
11302                ..Default::default()
11303            },
11304            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11305        )
11306        .unwrap()
11307    });
11308
11309    let is_still_following = Rc::new(RefCell::new(true));
11310    let follower_edit_event_count = Rc::new(RefCell::new(0));
11311    let pending_update = Rc::new(RefCell::new(None));
11312    let leader_entity = leader.root(cx).unwrap();
11313    let follower_entity = follower.root(cx).unwrap();
11314    _ = follower.update(cx, {
11315        let update = pending_update.clone();
11316        let is_still_following = is_still_following.clone();
11317        let follower_edit_event_count = follower_edit_event_count.clone();
11318        |_, window, cx| {
11319            cx.subscribe_in(
11320                &leader_entity,
11321                window,
11322                move |_, leader, event, window, cx| {
11323                    leader.read(cx).add_event_to_update_proto(
11324                        event,
11325                        &mut update.borrow_mut(),
11326                        window,
11327                        cx,
11328                    );
11329                },
11330            )
11331            .detach();
11332
11333            cx.subscribe_in(
11334                &follower_entity,
11335                window,
11336                move |_, _, event: &EditorEvent, _window, _cx| {
11337                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11338                        *is_still_following.borrow_mut() = false;
11339                    }
11340
11341                    if let EditorEvent::BufferEdited = event {
11342                        *follower_edit_event_count.borrow_mut() += 1;
11343                    }
11344                },
11345            )
11346            .detach();
11347        }
11348    });
11349
11350    // Update the selections only
11351    _ = leader.update(cx, |leader, window, cx| {
11352        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11353    });
11354    follower
11355        .update(cx, |follower, window, cx| {
11356            follower.apply_update_proto(
11357                &project,
11358                pending_update.borrow_mut().take().unwrap(),
11359                window,
11360                cx,
11361            )
11362        })
11363        .unwrap()
11364        .await
11365        .unwrap();
11366    _ = follower.update(cx, |follower, _, cx| {
11367        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11368    });
11369    assert!(*is_still_following.borrow());
11370    assert_eq!(*follower_edit_event_count.borrow(), 0);
11371
11372    // Update the scroll position only
11373    _ = leader.update(cx, |leader, window, cx| {
11374        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11375    });
11376    follower
11377        .update(cx, |follower, window, cx| {
11378            follower.apply_update_proto(
11379                &project,
11380                pending_update.borrow_mut().take().unwrap(),
11381                window,
11382                cx,
11383            )
11384        })
11385        .unwrap()
11386        .await
11387        .unwrap();
11388    assert_eq!(
11389        follower
11390            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11391            .unwrap(),
11392        gpui::Point::new(1.5, 3.5)
11393    );
11394    assert!(*is_still_following.borrow());
11395    assert_eq!(*follower_edit_event_count.borrow(), 0);
11396
11397    // Update the selections and scroll position. The follower's scroll position is updated
11398    // via autoscroll, not via the leader's exact scroll position.
11399    _ = leader.update(cx, |leader, window, cx| {
11400        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11401        leader.request_autoscroll(Autoscroll::newest(), cx);
11402        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11403    });
11404    follower
11405        .update(cx, |follower, window, cx| {
11406            follower.apply_update_proto(
11407                &project,
11408                pending_update.borrow_mut().take().unwrap(),
11409                window,
11410                cx,
11411            )
11412        })
11413        .unwrap()
11414        .await
11415        .unwrap();
11416    _ = follower.update(cx, |follower, _, cx| {
11417        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11418        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11419    });
11420    assert!(*is_still_following.borrow());
11421
11422    // Creating a pending selection that precedes another selection
11423    _ = leader.update(cx, |leader, window, cx| {
11424        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11425        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11426    });
11427    follower
11428        .update(cx, |follower, window, cx| {
11429            follower.apply_update_proto(
11430                &project,
11431                pending_update.borrow_mut().take().unwrap(),
11432                window,
11433                cx,
11434            )
11435        })
11436        .unwrap()
11437        .await
11438        .unwrap();
11439    _ = follower.update(cx, |follower, _, cx| {
11440        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11441    });
11442    assert!(*is_still_following.borrow());
11443
11444    // Extend the pending selection so that it surrounds another selection
11445    _ = leader.update(cx, |leader, window, cx| {
11446        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11447    });
11448    follower
11449        .update(cx, |follower, window, cx| {
11450            follower.apply_update_proto(
11451                &project,
11452                pending_update.borrow_mut().take().unwrap(),
11453                window,
11454                cx,
11455            )
11456        })
11457        .unwrap()
11458        .await
11459        .unwrap();
11460    _ = follower.update(cx, |follower, _, cx| {
11461        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11462    });
11463
11464    // Scrolling locally breaks the follow
11465    _ = follower.update(cx, |follower, window, cx| {
11466        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11467        follower.set_scroll_anchor(
11468            ScrollAnchor {
11469                anchor: top_anchor,
11470                offset: gpui::Point::new(0.0, 0.5),
11471            },
11472            window,
11473            cx,
11474        );
11475    });
11476    assert!(!(*is_still_following.borrow()));
11477}
11478
11479#[gpui::test]
11480async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11481    init_test(cx, |_| {});
11482
11483    let fs = FakeFs::new(cx.executor());
11484    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11485    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11486    let pane = workspace
11487        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11488        .unwrap();
11489
11490    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11491
11492    let leader = pane.update_in(cx, |_, window, cx| {
11493        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11494        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11495    });
11496
11497    // Start following the editor when it has no excerpts.
11498    let mut state_message =
11499        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11500    let workspace_entity = workspace.root(cx).unwrap();
11501    let follower_1 = cx
11502        .update_window(*workspace.deref(), |_, window, cx| {
11503            Editor::from_state_proto(
11504                workspace_entity,
11505                ViewId {
11506                    creator: Default::default(),
11507                    id: 0,
11508                },
11509                &mut state_message,
11510                window,
11511                cx,
11512            )
11513        })
11514        .unwrap()
11515        .unwrap()
11516        .await
11517        .unwrap();
11518
11519    let update_message = Rc::new(RefCell::new(None));
11520    follower_1.update_in(cx, {
11521        let update = update_message.clone();
11522        |_, window, cx| {
11523            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11524                leader.read(cx).add_event_to_update_proto(
11525                    event,
11526                    &mut update.borrow_mut(),
11527                    window,
11528                    cx,
11529                );
11530            })
11531            .detach();
11532        }
11533    });
11534
11535    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11536        (
11537            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11538            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11539        )
11540    });
11541
11542    // Insert some excerpts.
11543    leader.update(cx, |leader, cx| {
11544        leader.buffer.update(cx, |multibuffer, cx| {
11545            let excerpt_ids = multibuffer.push_excerpts(
11546                buffer_1.clone(),
11547                [
11548                    ExcerptRange::new(1..6),
11549                    ExcerptRange::new(12..15),
11550                    ExcerptRange::new(0..3),
11551                ],
11552                cx,
11553            );
11554            multibuffer.insert_excerpts_after(
11555                excerpt_ids[0],
11556                buffer_2.clone(),
11557                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11558                cx,
11559            );
11560        });
11561    });
11562
11563    // Apply the update of adding the excerpts.
11564    follower_1
11565        .update_in(cx, |follower, window, cx| {
11566            follower.apply_update_proto(
11567                &project,
11568                update_message.borrow().clone().unwrap(),
11569                window,
11570                cx,
11571            )
11572        })
11573        .await
11574        .unwrap();
11575    assert_eq!(
11576        follower_1.update(cx, |editor, cx| editor.text(cx)),
11577        leader.update(cx, |editor, cx| editor.text(cx))
11578    );
11579    update_message.borrow_mut().take();
11580
11581    // Start following separately after it already has excerpts.
11582    let mut state_message =
11583        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11584    let workspace_entity = workspace.root(cx).unwrap();
11585    let follower_2 = cx
11586        .update_window(*workspace.deref(), |_, window, cx| {
11587            Editor::from_state_proto(
11588                workspace_entity,
11589                ViewId {
11590                    creator: Default::default(),
11591                    id: 0,
11592                },
11593                &mut state_message,
11594                window,
11595                cx,
11596            )
11597        })
11598        .unwrap()
11599        .unwrap()
11600        .await
11601        .unwrap();
11602    assert_eq!(
11603        follower_2.update(cx, |editor, cx| editor.text(cx)),
11604        leader.update(cx, |editor, cx| editor.text(cx))
11605    );
11606
11607    // Remove some excerpts.
11608    leader.update(cx, |leader, cx| {
11609        leader.buffer.update(cx, |multibuffer, cx| {
11610            let excerpt_ids = multibuffer.excerpt_ids();
11611            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11612            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11613        });
11614    });
11615
11616    // Apply the update of removing the excerpts.
11617    follower_1
11618        .update_in(cx, |follower, window, cx| {
11619            follower.apply_update_proto(
11620                &project,
11621                update_message.borrow().clone().unwrap(),
11622                window,
11623                cx,
11624            )
11625        })
11626        .await
11627        .unwrap();
11628    follower_2
11629        .update_in(cx, |follower, window, cx| {
11630            follower.apply_update_proto(
11631                &project,
11632                update_message.borrow().clone().unwrap(),
11633                window,
11634                cx,
11635            )
11636        })
11637        .await
11638        .unwrap();
11639    update_message.borrow_mut().take();
11640    assert_eq!(
11641        follower_1.update(cx, |editor, cx| editor.text(cx)),
11642        leader.update(cx, |editor, cx| editor.text(cx))
11643    );
11644}
11645
11646#[gpui::test]
11647async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11648    init_test(cx, |_| {});
11649
11650    let mut cx = EditorTestContext::new(cx).await;
11651    let lsp_store =
11652        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11653
11654    cx.set_state(indoc! {"
11655        ˇfn func(abc def: i32) -> u32 {
11656        }
11657    "});
11658
11659    cx.update(|_, cx| {
11660        lsp_store.update(cx, |lsp_store, cx| {
11661            lsp_store
11662                .update_diagnostics(
11663                    LanguageServerId(0),
11664                    lsp::PublishDiagnosticsParams {
11665                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11666                        version: None,
11667                        diagnostics: vec![
11668                            lsp::Diagnostic {
11669                                range: lsp::Range::new(
11670                                    lsp::Position::new(0, 11),
11671                                    lsp::Position::new(0, 12),
11672                                ),
11673                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11674                                ..Default::default()
11675                            },
11676                            lsp::Diagnostic {
11677                                range: lsp::Range::new(
11678                                    lsp::Position::new(0, 12),
11679                                    lsp::Position::new(0, 15),
11680                                ),
11681                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11682                                ..Default::default()
11683                            },
11684                            lsp::Diagnostic {
11685                                range: lsp::Range::new(
11686                                    lsp::Position::new(0, 25),
11687                                    lsp::Position::new(0, 28),
11688                                ),
11689                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11690                                ..Default::default()
11691                            },
11692                        ],
11693                    },
11694                    &[],
11695                    cx,
11696                )
11697                .unwrap()
11698        });
11699    });
11700
11701    executor.run_until_parked();
11702
11703    cx.update_editor(|editor, window, cx| {
11704        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11705    });
11706
11707    cx.assert_editor_state(indoc! {"
11708        fn func(abc def: i32) -> ˇu32 {
11709        }
11710    "});
11711
11712    cx.update_editor(|editor, window, cx| {
11713        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11714    });
11715
11716    cx.assert_editor_state(indoc! {"
11717        fn func(abc ˇdef: i32) -> u32 {
11718        }
11719    "});
11720
11721    cx.update_editor(|editor, window, cx| {
11722        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11723    });
11724
11725    cx.assert_editor_state(indoc! {"
11726        fn func(abcˇ def: i32) -> u32 {
11727        }
11728    "});
11729
11730    cx.update_editor(|editor, window, cx| {
11731        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11732    });
11733
11734    cx.assert_editor_state(indoc! {"
11735        fn func(abc def: i32) -> ˇu32 {
11736        }
11737    "});
11738}
11739
11740#[gpui::test]
11741async fn cycle_through_same_place_diagnostics(
11742    executor: BackgroundExecutor,
11743    cx: &mut TestAppContext,
11744) {
11745    init_test(cx, |_| {});
11746
11747    let mut cx = EditorTestContext::new(cx).await;
11748    let lsp_store =
11749        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11750
11751    cx.set_state(indoc! {"
11752        ˇfn func(abc def: i32) -> u32 {
11753        }
11754    "});
11755
11756    cx.update(|_, cx| {
11757        lsp_store.update(cx, |lsp_store, cx| {
11758            lsp_store
11759                .update_diagnostics(
11760                    LanguageServerId(0),
11761                    lsp::PublishDiagnosticsParams {
11762                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11763                        version: None,
11764                        diagnostics: vec![
11765                            lsp::Diagnostic {
11766                                range: lsp::Range::new(
11767                                    lsp::Position::new(0, 11),
11768                                    lsp::Position::new(0, 12),
11769                                ),
11770                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11771                                ..Default::default()
11772                            },
11773                            lsp::Diagnostic {
11774                                range: lsp::Range::new(
11775                                    lsp::Position::new(0, 12),
11776                                    lsp::Position::new(0, 15),
11777                                ),
11778                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11779                                ..Default::default()
11780                            },
11781                            lsp::Diagnostic {
11782                                range: lsp::Range::new(
11783                                    lsp::Position::new(0, 12),
11784                                    lsp::Position::new(0, 15),
11785                                ),
11786                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11787                                ..Default::default()
11788                            },
11789                            lsp::Diagnostic {
11790                                range: lsp::Range::new(
11791                                    lsp::Position::new(0, 25),
11792                                    lsp::Position::new(0, 28),
11793                                ),
11794                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11795                                ..Default::default()
11796                            },
11797                        ],
11798                    },
11799                    &[],
11800                    cx,
11801                )
11802                .unwrap()
11803        });
11804    });
11805    executor.run_until_parked();
11806
11807    //// Backward
11808
11809    // Fourth diagnostic
11810    cx.update_editor(|editor, window, cx| {
11811        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11812    });
11813    cx.assert_editor_state(indoc! {"
11814        fn func(abc def: i32) -> ˇu32 {
11815        }
11816    "});
11817
11818    // Third diagnostic
11819    cx.update_editor(|editor, window, cx| {
11820        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11821    });
11822    cx.assert_editor_state(indoc! {"
11823        fn func(abc ˇdef: i32) -> u32 {
11824        }
11825    "});
11826
11827    // Second diagnostic, same place
11828    cx.update_editor(|editor, window, cx| {
11829        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11830    });
11831    cx.assert_editor_state(indoc! {"
11832        fn func(abc ˇdef: i32) -> u32 {
11833        }
11834    "});
11835
11836    // First diagnostic
11837    cx.update_editor(|editor, window, cx| {
11838        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11839    });
11840    cx.assert_editor_state(indoc! {"
11841        fn func(abcˇ def: i32) -> u32 {
11842        }
11843    "});
11844
11845    // Wrapped over, fourth diagnostic
11846    cx.update_editor(|editor, window, cx| {
11847        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11848    });
11849    cx.assert_editor_state(indoc! {"
11850        fn func(abc def: i32) -> ˇu32 {
11851        }
11852    "});
11853
11854    cx.update_editor(|editor, window, cx| {
11855        editor.move_to_beginning(&MoveToBeginning, window, cx);
11856    });
11857    cx.assert_editor_state(indoc! {"
11858        ˇfn func(abc def: i32) -> u32 {
11859        }
11860    "});
11861
11862    //// Forward
11863
11864    // First diagnostic
11865    cx.update_editor(|editor, window, cx| {
11866        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11867    });
11868    cx.assert_editor_state(indoc! {"
11869        fn func(abcˇ def: i32) -> u32 {
11870        }
11871    "});
11872
11873    // Second diagnostic
11874    cx.update_editor(|editor, window, cx| {
11875        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11876    });
11877    cx.assert_editor_state(indoc! {"
11878        fn func(abc ˇdef: i32) -> u32 {
11879        }
11880    "});
11881
11882    // Third diagnostic, same place
11883    cx.update_editor(|editor, window, cx| {
11884        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11885    });
11886    cx.assert_editor_state(indoc! {"
11887        fn func(abc ˇdef: i32) -> u32 {
11888        }
11889    "});
11890
11891    // Fourth diagnostic
11892    cx.update_editor(|editor, window, cx| {
11893        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11894    });
11895    cx.assert_editor_state(indoc! {"
11896        fn func(abc def: i32) -> ˇu32 {
11897        }
11898    "});
11899
11900    // Wrapped around, first diagnostic
11901    cx.update_editor(|editor, window, cx| {
11902        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11903    });
11904    cx.assert_editor_state(indoc! {"
11905        fn func(abcˇ def: i32) -> u32 {
11906        }
11907    "});
11908}
11909
11910#[gpui::test]
11911async fn active_diagnostics_dismiss_after_invalidation(
11912    executor: BackgroundExecutor,
11913    cx: &mut TestAppContext,
11914) {
11915    init_test(cx, |_| {});
11916
11917    let mut cx = EditorTestContext::new(cx).await;
11918    let lsp_store =
11919        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11920
11921    cx.set_state(indoc! {"
11922        ˇfn func(abc def: i32) -> u32 {
11923        }
11924    "});
11925
11926    let message = "Something's wrong!";
11927    cx.update(|_, cx| {
11928        lsp_store.update(cx, |lsp_store, cx| {
11929            lsp_store
11930                .update_diagnostics(
11931                    LanguageServerId(0),
11932                    lsp::PublishDiagnosticsParams {
11933                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11934                        version: None,
11935                        diagnostics: vec![lsp::Diagnostic {
11936                            range: lsp::Range::new(
11937                                lsp::Position::new(0, 11),
11938                                lsp::Position::new(0, 12),
11939                            ),
11940                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11941                            message: message.to_string(),
11942                            ..Default::default()
11943                        }],
11944                    },
11945                    &[],
11946                    cx,
11947                )
11948                .unwrap()
11949        });
11950    });
11951    executor.run_until_parked();
11952
11953    cx.update_editor(|editor, window, cx| {
11954        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11955        assert_eq!(
11956            editor
11957                .active_diagnostics
11958                .as_ref()
11959                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11960            Some(message),
11961            "Should have a diagnostics group activated"
11962        );
11963    });
11964    cx.assert_editor_state(indoc! {"
11965        fn func(abcˇ def: i32) -> u32 {
11966        }
11967    "});
11968
11969    cx.update(|_, cx| {
11970        lsp_store.update(cx, |lsp_store, cx| {
11971            lsp_store
11972                .update_diagnostics(
11973                    LanguageServerId(0),
11974                    lsp::PublishDiagnosticsParams {
11975                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11976                        version: None,
11977                        diagnostics: Vec::new(),
11978                    },
11979                    &[],
11980                    cx,
11981                )
11982                .unwrap()
11983        });
11984    });
11985    executor.run_until_parked();
11986    cx.update_editor(|editor, _, _| {
11987        assert_eq!(
11988            editor.active_diagnostics, None,
11989            "After no diagnostics set to the editor, no diagnostics should be active"
11990        );
11991    });
11992    cx.assert_editor_state(indoc! {"
11993        fn func(abcˇ def: i32) -> u32 {
11994        }
11995    "});
11996
11997    cx.update_editor(|editor, window, cx| {
11998        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11999        assert_eq!(
12000            editor.active_diagnostics, None,
12001            "Should be no diagnostics to go to and activate"
12002        );
12003    });
12004    cx.assert_editor_state(indoc! {"
12005        fn func(abcˇ def: i32) -> u32 {
12006        }
12007    "});
12008}
12009
12010#[gpui::test]
12011async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12012    init_test(cx, |_| {});
12013
12014    let mut cx = EditorTestContext::new(cx).await;
12015
12016    cx.set_state(indoc! {"
12017        fn func(abˇc def: i32) -> u32 {
12018        }
12019    "});
12020    let lsp_store =
12021        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12022
12023    cx.update(|_, cx| {
12024        lsp_store.update(cx, |lsp_store, cx| {
12025            lsp_store.update_diagnostics(
12026                LanguageServerId(0),
12027                lsp::PublishDiagnosticsParams {
12028                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12029                    version: None,
12030                    diagnostics: vec![lsp::Diagnostic {
12031                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12032                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12033                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12034                        ..Default::default()
12035                    }],
12036                },
12037                &[],
12038                cx,
12039            )
12040        })
12041    }).unwrap();
12042    cx.run_until_parked();
12043    cx.update_editor(|editor, window, cx| {
12044        hover_popover::hover(editor, &Default::default(), window, cx)
12045    });
12046    cx.run_until_parked();
12047    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12048}
12049
12050#[gpui::test]
12051async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12052    init_test(cx, |_| {});
12053
12054    let mut cx = EditorTestContext::new(cx).await;
12055
12056    let diff_base = r#"
12057        use some::mod;
12058
12059        const A: u32 = 42;
12060
12061        fn main() {
12062            println!("hello");
12063
12064            println!("world");
12065        }
12066        "#
12067    .unindent();
12068
12069    // Edits are modified, removed, modified, added
12070    cx.set_state(
12071        &r#"
12072        use some::modified;
12073
12074        ˇ
12075        fn main() {
12076            println!("hello there");
12077
12078            println!("around the");
12079            println!("world");
12080        }
12081        "#
12082        .unindent(),
12083    );
12084
12085    cx.set_head_text(&diff_base);
12086    executor.run_until_parked();
12087
12088    cx.update_editor(|editor, window, cx| {
12089        //Wrap around the bottom of the buffer
12090        for _ in 0..3 {
12091            editor.go_to_next_hunk(&GoToHunk, window, cx);
12092        }
12093    });
12094
12095    cx.assert_editor_state(
12096        &r#"
12097        ˇuse some::modified;
12098
12099
12100        fn main() {
12101            println!("hello there");
12102
12103            println!("around the");
12104            println!("world");
12105        }
12106        "#
12107        .unindent(),
12108    );
12109
12110    cx.update_editor(|editor, window, cx| {
12111        //Wrap around the top of the buffer
12112        for _ in 0..2 {
12113            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12114        }
12115    });
12116
12117    cx.assert_editor_state(
12118        &r#"
12119        use some::modified;
12120
12121
12122        fn main() {
12123        ˇ    println!("hello there");
12124
12125            println!("around the");
12126            println!("world");
12127        }
12128        "#
12129        .unindent(),
12130    );
12131
12132    cx.update_editor(|editor, window, cx| {
12133        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12134    });
12135
12136    cx.assert_editor_state(
12137        &r#"
12138        use some::modified;
12139
12140        ˇ
12141        fn main() {
12142            println!("hello there");
12143
12144            println!("around the");
12145            println!("world");
12146        }
12147        "#
12148        .unindent(),
12149    );
12150
12151    cx.update_editor(|editor, window, cx| {
12152        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12153    });
12154
12155    cx.assert_editor_state(
12156        &r#"
12157        ˇuse some::modified;
12158
12159
12160        fn main() {
12161            println!("hello there");
12162
12163            println!("around the");
12164            println!("world");
12165        }
12166        "#
12167        .unindent(),
12168    );
12169
12170    cx.update_editor(|editor, window, cx| {
12171        for _ in 0..2 {
12172            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12173        }
12174    });
12175
12176    cx.assert_editor_state(
12177        &r#"
12178        use some::modified;
12179
12180
12181        fn main() {
12182        ˇ    println!("hello there");
12183
12184            println!("around the");
12185            println!("world");
12186        }
12187        "#
12188        .unindent(),
12189    );
12190
12191    cx.update_editor(|editor, window, cx| {
12192        editor.fold(&Fold, window, cx);
12193    });
12194
12195    cx.update_editor(|editor, window, cx| {
12196        editor.go_to_next_hunk(&GoToHunk, window, cx);
12197    });
12198
12199    cx.assert_editor_state(
12200        &r#"
12201        ˇuse some::modified;
12202
12203
12204        fn main() {
12205            println!("hello there");
12206
12207            println!("around the");
12208            println!("world");
12209        }
12210        "#
12211        .unindent(),
12212    );
12213}
12214
12215#[test]
12216fn test_split_words() {
12217    fn split(text: &str) -> Vec<&str> {
12218        split_words(text).collect()
12219    }
12220
12221    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12222    assert_eq!(split("hello_world"), &["hello_", "world"]);
12223    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12224    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12225    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12226    assert_eq!(split("helloworld"), &["helloworld"]);
12227
12228    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12229}
12230
12231#[gpui::test]
12232async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12233    init_test(cx, |_| {});
12234
12235    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12236    let mut assert = |before, after| {
12237        let _state_context = cx.set_state(before);
12238        cx.run_until_parked();
12239        cx.update_editor(|editor, window, cx| {
12240            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12241        });
12242        cx.run_until_parked();
12243        cx.assert_editor_state(after);
12244    };
12245
12246    // Outside bracket jumps to outside of matching bracket
12247    assert("console.logˇ(var);", "console.log(var)ˇ;");
12248    assert("console.log(var)ˇ;", "console.logˇ(var);");
12249
12250    // Inside bracket jumps to inside of matching bracket
12251    assert("console.log(ˇvar);", "console.log(varˇ);");
12252    assert("console.log(varˇ);", "console.log(ˇvar);");
12253
12254    // When outside a bracket and inside, favor jumping to the inside bracket
12255    assert(
12256        "console.log('foo', [1, 2, 3]ˇ);",
12257        "console.log(ˇ'foo', [1, 2, 3]);",
12258    );
12259    assert(
12260        "console.log(ˇ'foo', [1, 2, 3]);",
12261        "console.log('foo', [1, 2, 3]ˇ);",
12262    );
12263
12264    // Bias forward if two options are equally likely
12265    assert(
12266        "let result = curried_fun()ˇ();",
12267        "let result = curried_fun()()ˇ;",
12268    );
12269
12270    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12271    assert(
12272        indoc! {"
12273            function test() {
12274                console.log('test')ˇ
12275            }"},
12276        indoc! {"
12277            function test() {
12278                console.logˇ('test')
12279            }"},
12280    );
12281}
12282
12283#[gpui::test]
12284async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12285    init_test(cx, |_| {});
12286
12287    let fs = FakeFs::new(cx.executor());
12288    fs.insert_tree(
12289        path!("/a"),
12290        json!({
12291            "main.rs": "fn main() { let a = 5; }",
12292            "other.rs": "// Test file",
12293        }),
12294    )
12295    .await;
12296    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12297
12298    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12299    language_registry.add(Arc::new(Language::new(
12300        LanguageConfig {
12301            name: "Rust".into(),
12302            matcher: LanguageMatcher {
12303                path_suffixes: vec!["rs".to_string()],
12304                ..Default::default()
12305            },
12306            brackets: BracketPairConfig {
12307                pairs: vec![BracketPair {
12308                    start: "{".to_string(),
12309                    end: "}".to_string(),
12310                    close: true,
12311                    surround: true,
12312                    newline: true,
12313                }],
12314                disabled_scopes_by_bracket_ix: Vec::new(),
12315            },
12316            ..Default::default()
12317        },
12318        Some(tree_sitter_rust::LANGUAGE.into()),
12319    )));
12320    let mut fake_servers = language_registry.register_fake_lsp(
12321        "Rust",
12322        FakeLspAdapter {
12323            capabilities: lsp::ServerCapabilities {
12324                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12325                    first_trigger_character: "{".to_string(),
12326                    more_trigger_character: None,
12327                }),
12328                ..Default::default()
12329            },
12330            ..Default::default()
12331        },
12332    );
12333
12334    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12335
12336    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12337
12338    let worktree_id = workspace
12339        .update(cx, |workspace, _, cx| {
12340            workspace.project().update(cx, |project, cx| {
12341                project.worktrees(cx).next().unwrap().read(cx).id()
12342            })
12343        })
12344        .unwrap();
12345
12346    let buffer = project
12347        .update(cx, |project, cx| {
12348            project.open_local_buffer(path!("/a/main.rs"), cx)
12349        })
12350        .await
12351        .unwrap();
12352    let editor_handle = workspace
12353        .update(cx, |workspace, window, cx| {
12354            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12355        })
12356        .unwrap()
12357        .await
12358        .unwrap()
12359        .downcast::<Editor>()
12360        .unwrap();
12361
12362    cx.executor().start_waiting();
12363    let fake_server = fake_servers.next().await.unwrap();
12364
12365    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12366        |params, _| async move {
12367            assert_eq!(
12368                params.text_document_position.text_document.uri,
12369                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12370            );
12371            assert_eq!(
12372                params.text_document_position.position,
12373                lsp::Position::new(0, 21),
12374            );
12375
12376            Ok(Some(vec![lsp::TextEdit {
12377                new_text: "]".to_string(),
12378                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12379            }]))
12380        },
12381    );
12382
12383    editor_handle.update_in(cx, |editor, window, cx| {
12384        window.focus(&editor.focus_handle(cx));
12385        editor.change_selections(None, window, cx, |s| {
12386            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12387        });
12388        editor.handle_input("{", window, cx);
12389    });
12390
12391    cx.executor().run_until_parked();
12392
12393    buffer.update(cx, |buffer, _| {
12394        assert_eq!(
12395            buffer.text(),
12396            "fn main() { let a = {5}; }",
12397            "No extra braces from on type formatting should appear in the buffer"
12398        )
12399    });
12400}
12401
12402#[gpui::test]
12403async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12404    init_test(cx, |_| {});
12405
12406    let fs = FakeFs::new(cx.executor());
12407    fs.insert_tree(
12408        path!("/a"),
12409        json!({
12410            "main.rs": "fn main() { let a = 5; }",
12411            "other.rs": "// Test file",
12412        }),
12413    )
12414    .await;
12415
12416    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12417
12418    let server_restarts = Arc::new(AtomicUsize::new(0));
12419    let closure_restarts = Arc::clone(&server_restarts);
12420    let language_server_name = "test language server";
12421    let language_name: LanguageName = "Rust".into();
12422
12423    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12424    language_registry.add(Arc::new(Language::new(
12425        LanguageConfig {
12426            name: language_name.clone(),
12427            matcher: LanguageMatcher {
12428                path_suffixes: vec!["rs".to_string()],
12429                ..Default::default()
12430            },
12431            ..Default::default()
12432        },
12433        Some(tree_sitter_rust::LANGUAGE.into()),
12434    )));
12435    let mut fake_servers = language_registry.register_fake_lsp(
12436        "Rust",
12437        FakeLspAdapter {
12438            name: language_server_name,
12439            initialization_options: Some(json!({
12440                "testOptionValue": true
12441            })),
12442            initializer: Some(Box::new(move |fake_server| {
12443                let task_restarts = Arc::clone(&closure_restarts);
12444                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12445                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12446                    futures::future::ready(Ok(()))
12447                });
12448            })),
12449            ..Default::default()
12450        },
12451    );
12452
12453    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12454    let _buffer = project
12455        .update(cx, |project, cx| {
12456            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12457        })
12458        .await
12459        .unwrap();
12460    let _fake_server = fake_servers.next().await.unwrap();
12461    update_test_language_settings(cx, |language_settings| {
12462        language_settings.languages.insert(
12463            language_name.clone(),
12464            LanguageSettingsContent {
12465                tab_size: NonZeroU32::new(8),
12466                ..Default::default()
12467            },
12468        );
12469    });
12470    cx.executor().run_until_parked();
12471    assert_eq!(
12472        server_restarts.load(atomic::Ordering::Acquire),
12473        0,
12474        "Should not restart LSP server on an unrelated change"
12475    );
12476
12477    update_test_project_settings(cx, |project_settings| {
12478        project_settings.lsp.insert(
12479            "Some other server name".into(),
12480            LspSettings {
12481                binary: None,
12482                settings: None,
12483                initialization_options: Some(json!({
12484                    "some other init value": false
12485                })),
12486            },
12487        );
12488    });
12489    cx.executor().run_until_parked();
12490    assert_eq!(
12491        server_restarts.load(atomic::Ordering::Acquire),
12492        0,
12493        "Should not restart LSP server on an unrelated LSP settings change"
12494    );
12495
12496    update_test_project_settings(cx, |project_settings| {
12497        project_settings.lsp.insert(
12498            language_server_name.into(),
12499            LspSettings {
12500                binary: None,
12501                settings: None,
12502                initialization_options: Some(json!({
12503                    "anotherInitValue": false
12504                })),
12505            },
12506        );
12507    });
12508    cx.executor().run_until_parked();
12509    assert_eq!(
12510        server_restarts.load(atomic::Ordering::Acquire),
12511        1,
12512        "Should restart LSP server on a related LSP settings change"
12513    );
12514
12515    update_test_project_settings(cx, |project_settings| {
12516        project_settings.lsp.insert(
12517            language_server_name.into(),
12518            LspSettings {
12519                binary: None,
12520                settings: None,
12521                initialization_options: Some(json!({
12522                    "anotherInitValue": false
12523                })),
12524            },
12525        );
12526    });
12527    cx.executor().run_until_parked();
12528    assert_eq!(
12529        server_restarts.load(atomic::Ordering::Acquire),
12530        1,
12531        "Should not restart LSP server on a related LSP settings change that is the same"
12532    );
12533
12534    update_test_project_settings(cx, |project_settings| {
12535        project_settings.lsp.insert(
12536            language_server_name.into(),
12537            LspSettings {
12538                binary: None,
12539                settings: None,
12540                initialization_options: None,
12541            },
12542        );
12543    });
12544    cx.executor().run_until_parked();
12545    assert_eq!(
12546        server_restarts.load(atomic::Ordering::Acquire),
12547        2,
12548        "Should restart LSP server on another related LSP settings change"
12549    );
12550}
12551
12552#[gpui::test]
12553async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12554    init_test(cx, |_| {});
12555
12556    let mut cx = EditorLspTestContext::new_rust(
12557        lsp::ServerCapabilities {
12558            completion_provider: Some(lsp::CompletionOptions {
12559                trigger_characters: Some(vec![".".to_string()]),
12560                resolve_provider: Some(true),
12561                ..Default::default()
12562            }),
12563            ..Default::default()
12564        },
12565        cx,
12566    )
12567    .await;
12568
12569    cx.set_state("fn main() { let a = 2ˇ; }");
12570    cx.simulate_keystroke(".");
12571    let completion_item = lsp::CompletionItem {
12572        label: "some".into(),
12573        kind: Some(lsp::CompletionItemKind::SNIPPET),
12574        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12575        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12576            kind: lsp::MarkupKind::Markdown,
12577            value: "```rust\nSome(2)\n```".to_string(),
12578        })),
12579        deprecated: Some(false),
12580        sort_text: Some("fffffff2".to_string()),
12581        filter_text: Some("some".to_string()),
12582        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12583        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12584            range: lsp::Range {
12585                start: lsp::Position {
12586                    line: 0,
12587                    character: 22,
12588                },
12589                end: lsp::Position {
12590                    line: 0,
12591                    character: 22,
12592                },
12593            },
12594            new_text: "Some(2)".to_string(),
12595        })),
12596        additional_text_edits: Some(vec![lsp::TextEdit {
12597            range: lsp::Range {
12598                start: lsp::Position {
12599                    line: 0,
12600                    character: 20,
12601                },
12602                end: lsp::Position {
12603                    line: 0,
12604                    character: 22,
12605                },
12606            },
12607            new_text: "".to_string(),
12608        }]),
12609        ..Default::default()
12610    };
12611
12612    let closure_completion_item = completion_item.clone();
12613    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12614        let task_completion_item = closure_completion_item.clone();
12615        async move {
12616            Ok(Some(lsp::CompletionResponse::Array(vec![
12617                task_completion_item,
12618            ])))
12619        }
12620    });
12621
12622    request.next().await;
12623
12624    cx.condition(|editor, _| editor.context_menu_visible())
12625        .await;
12626    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12627        editor
12628            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12629            .unwrap()
12630    });
12631    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12632
12633    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12634        let task_completion_item = completion_item.clone();
12635        async move { Ok(task_completion_item) }
12636    })
12637    .next()
12638    .await
12639    .unwrap();
12640    apply_additional_edits.await.unwrap();
12641    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12642}
12643
12644#[gpui::test]
12645async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12646    init_test(cx, |_| {});
12647
12648    let mut cx = EditorLspTestContext::new_rust(
12649        lsp::ServerCapabilities {
12650            completion_provider: Some(lsp::CompletionOptions {
12651                trigger_characters: Some(vec![".".to_string()]),
12652                resolve_provider: Some(true),
12653                ..Default::default()
12654            }),
12655            ..Default::default()
12656        },
12657        cx,
12658    )
12659    .await;
12660
12661    cx.set_state("fn main() { let a = 2ˇ; }");
12662    cx.simulate_keystroke(".");
12663
12664    let item1 = lsp::CompletionItem {
12665        label: "method id()".to_string(),
12666        filter_text: Some("id".to_string()),
12667        detail: None,
12668        documentation: None,
12669        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12670            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12671            new_text: ".id".to_string(),
12672        })),
12673        ..lsp::CompletionItem::default()
12674    };
12675
12676    let item2 = lsp::CompletionItem {
12677        label: "other".to_string(),
12678        filter_text: Some("other".to_string()),
12679        detail: None,
12680        documentation: None,
12681        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12682            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12683            new_text: ".other".to_string(),
12684        })),
12685        ..lsp::CompletionItem::default()
12686    };
12687
12688    let item1 = item1.clone();
12689    cx.set_request_handler::<lsp::request::Completion, _, _>({
12690        let item1 = item1.clone();
12691        move |_, _, _| {
12692            let item1 = item1.clone();
12693            let item2 = item2.clone();
12694            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12695        }
12696    })
12697    .next()
12698    .await;
12699
12700    cx.condition(|editor, _| editor.context_menu_visible())
12701        .await;
12702    cx.update_editor(|editor, _, _| {
12703        let context_menu = editor.context_menu.borrow_mut();
12704        let context_menu = context_menu
12705            .as_ref()
12706            .expect("Should have the context menu deployed");
12707        match context_menu {
12708            CodeContextMenu::Completions(completions_menu) => {
12709                let completions = completions_menu.completions.borrow_mut();
12710                assert_eq!(
12711                    completions
12712                        .iter()
12713                        .map(|completion| &completion.label.text)
12714                        .collect::<Vec<_>>(),
12715                    vec!["method id()", "other"]
12716                )
12717            }
12718            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12719        }
12720    });
12721
12722    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12723        let item1 = item1.clone();
12724        move |_, item_to_resolve, _| {
12725            let item1 = item1.clone();
12726            async move {
12727                if item1 == item_to_resolve {
12728                    Ok(lsp::CompletionItem {
12729                        label: "method id()".to_string(),
12730                        filter_text: Some("id".to_string()),
12731                        detail: Some("Now resolved!".to_string()),
12732                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12733                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12734                            range: lsp::Range::new(
12735                                lsp::Position::new(0, 22),
12736                                lsp::Position::new(0, 22),
12737                            ),
12738                            new_text: ".id".to_string(),
12739                        })),
12740                        ..lsp::CompletionItem::default()
12741                    })
12742                } else {
12743                    Ok(item_to_resolve)
12744                }
12745            }
12746        }
12747    })
12748    .next()
12749    .await
12750    .unwrap();
12751    cx.run_until_parked();
12752
12753    cx.update_editor(|editor, window, cx| {
12754        editor.context_menu_next(&Default::default(), window, cx);
12755    });
12756
12757    cx.update_editor(|editor, _, _| {
12758        let context_menu = editor.context_menu.borrow_mut();
12759        let context_menu = context_menu
12760            .as_ref()
12761            .expect("Should have the context menu deployed");
12762        match context_menu {
12763            CodeContextMenu::Completions(completions_menu) => {
12764                let completions = completions_menu.completions.borrow_mut();
12765                assert_eq!(
12766                    completions
12767                        .iter()
12768                        .map(|completion| &completion.label.text)
12769                        .collect::<Vec<_>>(),
12770                    vec!["method id() Now resolved!", "other"],
12771                    "Should update first completion label, but not second as the filter text did not match."
12772                );
12773            }
12774            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12775        }
12776    });
12777}
12778
12779#[gpui::test]
12780async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12781    init_test(cx, |_| {});
12782
12783    let mut cx = EditorLspTestContext::new_rust(
12784        lsp::ServerCapabilities {
12785            completion_provider: Some(lsp::CompletionOptions {
12786                trigger_characters: Some(vec![".".to_string()]),
12787                resolve_provider: Some(true),
12788                ..Default::default()
12789            }),
12790            ..Default::default()
12791        },
12792        cx,
12793    )
12794    .await;
12795
12796    cx.set_state("fn main() { let a = 2ˇ; }");
12797    cx.simulate_keystroke(".");
12798
12799    let unresolved_item_1 = lsp::CompletionItem {
12800        label: "id".to_string(),
12801        filter_text: Some("id".to_string()),
12802        detail: None,
12803        documentation: None,
12804        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12805            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12806            new_text: ".id".to_string(),
12807        })),
12808        ..lsp::CompletionItem::default()
12809    };
12810    let resolved_item_1 = lsp::CompletionItem {
12811        additional_text_edits: Some(vec![lsp::TextEdit {
12812            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12813            new_text: "!!".to_string(),
12814        }]),
12815        ..unresolved_item_1.clone()
12816    };
12817    let unresolved_item_2 = lsp::CompletionItem {
12818        label: "other".to_string(),
12819        filter_text: Some("other".to_string()),
12820        detail: None,
12821        documentation: None,
12822        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12823            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12824            new_text: ".other".to_string(),
12825        })),
12826        ..lsp::CompletionItem::default()
12827    };
12828    let resolved_item_2 = lsp::CompletionItem {
12829        additional_text_edits: Some(vec![lsp::TextEdit {
12830            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12831            new_text: "??".to_string(),
12832        }]),
12833        ..unresolved_item_2.clone()
12834    };
12835
12836    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12837    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12838    cx.lsp
12839        .server
12840        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12841            let unresolved_item_1 = unresolved_item_1.clone();
12842            let resolved_item_1 = resolved_item_1.clone();
12843            let unresolved_item_2 = unresolved_item_2.clone();
12844            let resolved_item_2 = resolved_item_2.clone();
12845            let resolve_requests_1 = resolve_requests_1.clone();
12846            let resolve_requests_2 = resolve_requests_2.clone();
12847            move |unresolved_request, _| {
12848                let unresolved_item_1 = unresolved_item_1.clone();
12849                let resolved_item_1 = resolved_item_1.clone();
12850                let unresolved_item_2 = unresolved_item_2.clone();
12851                let resolved_item_2 = resolved_item_2.clone();
12852                let resolve_requests_1 = resolve_requests_1.clone();
12853                let resolve_requests_2 = resolve_requests_2.clone();
12854                async move {
12855                    if unresolved_request == unresolved_item_1 {
12856                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12857                        Ok(resolved_item_1.clone())
12858                    } else if unresolved_request == unresolved_item_2 {
12859                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12860                        Ok(resolved_item_2.clone())
12861                    } else {
12862                        panic!("Unexpected completion item {unresolved_request:?}")
12863                    }
12864                }
12865            }
12866        })
12867        .detach();
12868
12869    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12870        let unresolved_item_1 = unresolved_item_1.clone();
12871        let unresolved_item_2 = unresolved_item_2.clone();
12872        async move {
12873            Ok(Some(lsp::CompletionResponse::Array(vec![
12874                unresolved_item_1,
12875                unresolved_item_2,
12876            ])))
12877        }
12878    })
12879    .next()
12880    .await;
12881
12882    cx.condition(|editor, _| editor.context_menu_visible())
12883        .await;
12884    cx.update_editor(|editor, _, _| {
12885        let context_menu = editor.context_menu.borrow_mut();
12886        let context_menu = context_menu
12887            .as_ref()
12888            .expect("Should have the context menu deployed");
12889        match context_menu {
12890            CodeContextMenu::Completions(completions_menu) => {
12891                let completions = completions_menu.completions.borrow_mut();
12892                assert_eq!(
12893                    completions
12894                        .iter()
12895                        .map(|completion| &completion.label.text)
12896                        .collect::<Vec<_>>(),
12897                    vec!["id", "other"]
12898                )
12899            }
12900            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12901        }
12902    });
12903    cx.run_until_parked();
12904
12905    cx.update_editor(|editor, window, cx| {
12906        editor.context_menu_next(&ContextMenuNext, window, cx);
12907    });
12908    cx.run_until_parked();
12909    cx.update_editor(|editor, window, cx| {
12910        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12911    });
12912    cx.run_until_parked();
12913    cx.update_editor(|editor, window, cx| {
12914        editor.context_menu_next(&ContextMenuNext, window, cx);
12915    });
12916    cx.run_until_parked();
12917    cx.update_editor(|editor, window, cx| {
12918        editor
12919            .compose_completion(&ComposeCompletion::default(), window, cx)
12920            .expect("No task returned")
12921    })
12922    .await
12923    .expect("Completion failed");
12924    cx.run_until_parked();
12925
12926    cx.update_editor(|editor, _, cx| {
12927        assert_eq!(
12928            resolve_requests_1.load(atomic::Ordering::Acquire),
12929            1,
12930            "Should always resolve once despite multiple selections"
12931        );
12932        assert_eq!(
12933            resolve_requests_2.load(atomic::Ordering::Acquire),
12934            1,
12935            "Should always resolve once after multiple selections and applying the completion"
12936        );
12937        assert_eq!(
12938            editor.text(cx),
12939            "fn main() { let a = ??.other; }",
12940            "Should use resolved data when applying the completion"
12941        );
12942    });
12943}
12944
12945#[gpui::test]
12946async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12947    init_test(cx, |_| {});
12948
12949    let item_0 = lsp::CompletionItem {
12950        label: "abs".into(),
12951        insert_text: Some("abs".into()),
12952        data: Some(json!({ "very": "special"})),
12953        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12954        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12955            lsp::InsertReplaceEdit {
12956                new_text: "abs".to_string(),
12957                insert: lsp::Range::default(),
12958                replace: lsp::Range::default(),
12959            },
12960        )),
12961        ..lsp::CompletionItem::default()
12962    };
12963    let items = iter::once(item_0.clone())
12964        .chain((11..51).map(|i| lsp::CompletionItem {
12965            label: format!("item_{}", i),
12966            insert_text: Some(format!("item_{}", i)),
12967            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12968            ..lsp::CompletionItem::default()
12969        }))
12970        .collect::<Vec<_>>();
12971
12972    let default_commit_characters = vec!["?".to_string()];
12973    let default_data = json!({ "default": "data"});
12974    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12975    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12976    let default_edit_range = lsp::Range {
12977        start: lsp::Position {
12978            line: 0,
12979            character: 5,
12980        },
12981        end: lsp::Position {
12982            line: 0,
12983            character: 5,
12984        },
12985    };
12986
12987    let mut cx = EditorLspTestContext::new_rust(
12988        lsp::ServerCapabilities {
12989            completion_provider: Some(lsp::CompletionOptions {
12990                trigger_characters: Some(vec![".".to_string()]),
12991                resolve_provider: Some(true),
12992                ..Default::default()
12993            }),
12994            ..Default::default()
12995        },
12996        cx,
12997    )
12998    .await;
12999
13000    cx.set_state("fn main() { let a = 2ˇ; }");
13001    cx.simulate_keystroke(".");
13002
13003    let completion_data = default_data.clone();
13004    let completion_characters = default_commit_characters.clone();
13005    let completion_items = items.clone();
13006    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13007        let default_data = completion_data.clone();
13008        let default_commit_characters = completion_characters.clone();
13009        let items = completion_items.clone();
13010        async move {
13011            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13012                items,
13013                item_defaults: Some(lsp::CompletionListItemDefaults {
13014                    data: Some(default_data.clone()),
13015                    commit_characters: Some(default_commit_characters.clone()),
13016                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13017                        default_edit_range,
13018                    )),
13019                    insert_text_format: Some(default_insert_text_format),
13020                    insert_text_mode: Some(default_insert_text_mode),
13021                }),
13022                ..lsp::CompletionList::default()
13023            })))
13024        }
13025    })
13026    .next()
13027    .await;
13028
13029    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13030    cx.lsp
13031        .server
13032        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13033            let closure_resolved_items = resolved_items.clone();
13034            move |item_to_resolve, _| {
13035                let closure_resolved_items = closure_resolved_items.clone();
13036                async move {
13037                    closure_resolved_items.lock().push(item_to_resolve.clone());
13038                    Ok(item_to_resolve)
13039                }
13040            }
13041        })
13042        .detach();
13043
13044    cx.condition(|editor, _| editor.context_menu_visible())
13045        .await;
13046    cx.run_until_parked();
13047    cx.update_editor(|editor, _, _| {
13048        let menu = editor.context_menu.borrow_mut();
13049        match menu.as_ref().expect("should have the completions menu") {
13050            CodeContextMenu::Completions(completions_menu) => {
13051                assert_eq!(
13052                    completions_menu
13053                        .entries
13054                        .borrow()
13055                        .iter()
13056                        .map(|mat| mat.string.clone())
13057                        .collect::<Vec<String>>(),
13058                    items
13059                        .iter()
13060                        .map(|completion| completion.label.clone())
13061                        .collect::<Vec<String>>()
13062                );
13063            }
13064            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13065        }
13066    });
13067    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13068    // with 4 from the end.
13069    assert_eq!(
13070        *resolved_items.lock(),
13071        [&items[0..16], &items[items.len() - 4..items.len()]]
13072            .concat()
13073            .iter()
13074            .cloned()
13075            .map(|mut item| {
13076                if item.data.is_none() {
13077                    item.data = Some(default_data.clone());
13078                }
13079                item
13080            })
13081            .collect::<Vec<lsp::CompletionItem>>(),
13082        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13083    );
13084    resolved_items.lock().clear();
13085
13086    cx.update_editor(|editor, window, cx| {
13087        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13088    });
13089    cx.run_until_parked();
13090    // Completions that have already been resolved are skipped.
13091    assert_eq!(
13092        *resolved_items.lock(),
13093        items[items.len() - 16..items.len() - 4]
13094            .iter()
13095            .cloned()
13096            .map(|mut item| {
13097                if item.data.is_none() {
13098                    item.data = Some(default_data.clone());
13099                }
13100                item
13101            })
13102            .collect::<Vec<lsp::CompletionItem>>()
13103    );
13104    resolved_items.lock().clear();
13105}
13106
13107#[gpui::test]
13108async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13109    init_test(cx, |_| {});
13110
13111    let mut cx = EditorLspTestContext::new(
13112        Language::new(
13113            LanguageConfig {
13114                matcher: LanguageMatcher {
13115                    path_suffixes: vec!["jsx".into()],
13116                    ..Default::default()
13117                },
13118                overrides: [(
13119                    "element".into(),
13120                    LanguageConfigOverride {
13121                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13122                        ..Default::default()
13123                    },
13124                )]
13125                .into_iter()
13126                .collect(),
13127                ..Default::default()
13128            },
13129            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13130        )
13131        .with_override_query("(jsx_self_closing_element) @element")
13132        .unwrap(),
13133        lsp::ServerCapabilities {
13134            completion_provider: Some(lsp::CompletionOptions {
13135                trigger_characters: Some(vec![":".to_string()]),
13136                ..Default::default()
13137            }),
13138            ..Default::default()
13139        },
13140        cx,
13141    )
13142    .await;
13143
13144    cx.lsp
13145        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13146            Ok(Some(lsp::CompletionResponse::Array(vec![
13147                lsp::CompletionItem {
13148                    label: "bg-blue".into(),
13149                    ..Default::default()
13150                },
13151                lsp::CompletionItem {
13152                    label: "bg-red".into(),
13153                    ..Default::default()
13154                },
13155                lsp::CompletionItem {
13156                    label: "bg-yellow".into(),
13157                    ..Default::default()
13158                },
13159            ])))
13160        });
13161
13162    cx.set_state(r#"<p class="bgˇ" />"#);
13163
13164    // Trigger completion when typing a dash, because the dash is an extra
13165    // word character in the 'element' scope, which contains the cursor.
13166    cx.simulate_keystroke("-");
13167    cx.executor().run_until_parked();
13168    cx.update_editor(|editor, _, _| {
13169        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13170        {
13171            assert_eq!(
13172                completion_menu_entries(&menu),
13173                &["bg-red", "bg-blue", "bg-yellow"]
13174            );
13175        } else {
13176            panic!("expected completion menu to be open");
13177        }
13178    });
13179
13180    cx.simulate_keystroke("l");
13181    cx.executor().run_until_parked();
13182    cx.update_editor(|editor, _, _| {
13183        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13184        {
13185            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13186        } else {
13187            panic!("expected completion menu to be open");
13188        }
13189    });
13190
13191    // When filtering completions, consider the character after the '-' to
13192    // be the start of a subword.
13193    cx.set_state(r#"<p class="yelˇ" />"#);
13194    cx.simulate_keystroke("l");
13195    cx.executor().run_until_parked();
13196    cx.update_editor(|editor, _, _| {
13197        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13198        {
13199            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13200        } else {
13201            panic!("expected completion menu to be open");
13202        }
13203    });
13204}
13205
13206fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13207    let entries = menu.entries.borrow();
13208    entries.iter().map(|mat| mat.string.clone()).collect()
13209}
13210
13211#[gpui::test]
13212async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13213    init_test(cx, |settings| {
13214        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13215            FormatterList(vec![Formatter::Prettier].into()),
13216        ))
13217    });
13218
13219    let fs = FakeFs::new(cx.executor());
13220    fs.insert_file(path!("/file.ts"), Default::default()).await;
13221
13222    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13223    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13224
13225    language_registry.add(Arc::new(Language::new(
13226        LanguageConfig {
13227            name: "TypeScript".into(),
13228            matcher: LanguageMatcher {
13229                path_suffixes: vec!["ts".to_string()],
13230                ..Default::default()
13231            },
13232            ..Default::default()
13233        },
13234        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13235    )));
13236    update_test_language_settings(cx, |settings| {
13237        settings.defaults.prettier = Some(PrettierSettings {
13238            allowed: true,
13239            ..PrettierSettings::default()
13240        });
13241    });
13242
13243    let test_plugin = "test_plugin";
13244    let _ = language_registry.register_fake_lsp(
13245        "TypeScript",
13246        FakeLspAdapter {
13247            prettier_plugins: vec![test_plugin],
13248            ..Default::default()
13249        },
13250    );
13251
13252    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13253    let buffer = project
13254        .update(cx, |project, cx| {
13255            project.open_local_buffer(path!("/file.ts"), cx)
13256        })
13257        .await
13258        .unwrap();
13259
13260    let buffer_text = "one\ntwo\nthree\n";
13261    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13262    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13263    editor.update_in(cx, |editor, window, cx| {
13264        editor.set_text(buffer_text, window, cx)
13265    });
13266
13267    editor
13268        .update_in(cx, |editor, window, cx| {
13269            editor.perform_format(
13270                project.clone(),
13271                FormatTrigger::Manual,
13272                FormatTarget::Buffers,
13273                window,
13274                cx,
13275            )
13276        })
13277        .unwrap()
13278        .await;
13279    assert_eq!(
13280        editor.update(cx, |editor, cx| editor.text(cx)),
13281        buffer_text.to_string() + prettier_format_suffix,
13282        "Test prettier formatting was not applied to the original buffer text",
13283    );
13284
13285    update_test_language_settings(cx, |settings| {
13286        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13287    });
13288    let format = editor.update_in(cx, |editor, window, cx| {
13289        editor.perform_format(
13290            project.clone(),
13291            FormatTrigger::Manual,
13292            FormatTarget::Buffers,
13293            window,
13294            cx,
13295        )
13296    });
13297    format.await.unwrap();
13298    assert_eq!(
13299        editor.update(cx, |editor, cx| editor.text(cx)),
13300        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13301        "Autoformatting (via test prettier) was not applied to the original buffer text",
13302    );
13303}
13304
13305#[gpui::test]
13306async fn test_addition_reverts(cx: &mut TestAppContext) {
13307    init_test(cx, |_| {});
13308    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13309    let base_text = indoc! {r#"
13310        struct Row;
13311        struct Row1;
13312        struct Row2;
13313
13314        struct Row4;
13315        struct Row5;
13316        struct Row6;
13317
13318        struct Row8;
13319        struct Row9;
13320        struct Row10;"#};
13321
13322    // When addition hunks are not adjacent to carets, no hunk revert is performed
13323    assert_hunk_revert(
13324        indoc! {r#"struct Row;
13325                   struct Row1;
13326                   struct Row1.1;
13327                   struct Row1.2;
13328                   struct Row2;ˇ
13329
13330                   struct Row4;
13331                   struct Row5;
13332                   struct Row6;
13333
13334                   struct Row8;
13335                   ˇstruct Row9;
13336                   struct Row9.1;
13337                   struct Row9.2;
13338                   struct Row9.3;
13339                   struct Row10;"#},
13340        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13341        indoc! {r#"struct Row;
13342                   struct Row1;
13343                   struct Row1.1;
13344                   struct Row1.2;
13345                   struct Row2;ˇ
13346
13347                   struct Row4;
13348                   struct Row5;
13349                   struct Row6;
13350
13351                   struct Row8;
13352                   ˇstruct Row9;
13353                   struct Row9.1;
13354                   struct Row9.2;
13355                   struct Row9.3;
13356                   struct Row10;"#},
13357        base_text,
13358        &mut cx,
13359    );
13360    // Same for selections
13361    assert_hunk_revert(
13362        indoc! {r#"struct Row;
13363                   struct Row1;
13364                   struct Row2;
13365                   struct Row2.1;
13366                   struct Row2.2;
13367                   «ˇ
13368                   struct Row4;
13369                   struct» Row5;
13370                   «struct Row6;
13371                   ˇ»
13372                   struct Row9.1;
13373                   struct Row9.2;
13374                   struct Row9.3;
13375                   struct Row8;
13376                   struct Row9;
13377                   struct Row10;"#},
13378        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13379        indoc! {r#"struct Row;
13380                   struct Row1;
13381                   struct Row2;
13382                   struct Row2.1;
13383                   struct Row2.2;
13384                   «ˇ
13385                   struct Row4;
13386                   struct» Row5;
13387                   «struct Row6;
13388                   ˇ»
13389                   struct Row9.1;
13390                   struct Row9.2;
13391                   struct Row9.3;
13392                   struct Row8;
13393                   struct Row9;
13394                   struct Row10;"#},
13395        base_text,
13396        &mut cx,
13397    );
13398
13399    // When carets and selections intersect the addition hunks, those are reverted.
13400    // Adjacent carets got merged.
13401    assert_hunk_revert(
13402        indoc! {r#"struct Row;
13403                   ˇ// something on the top
13404                   struct Row1;
13405                   struct Row2;
13406                   struct Roˇw3.1;
13407                   struct Row2.2;
13408                   struct Row2.3;ˇ
13409
13410                   struct Row4;
13411                   struct ˇRow5.1;
13412                   struct Row5.2;
13413                   struct «Rowˇ»5.3;
13414                   struct Row5;
13415                   struct Row6;
13416                   ˇ
13417                   struct Row9.1;
13418                   struct «Rowˇ»9.2;
13419                   struct «ˇRow»9.3;
13420                   struct Row8;
13421                   struct Row9;
13422                   «ˇ// something on bottom»
13423                   struct Row10;"#},
13424        vec![
13425            DiffHunkStatusKind::Added,
13426            DiffHunkStatusKind::Added,
13427            DiffHunkStatusKind::Added,
13428            DiffHunkStatusKind::Added,
13429            DiffHunkStatusKind::Added,
13430        ],
13431        indoc! {r#"struct Row;
13432                   ˇstruct Row1;
13433                   struct Row2;
13434                   ˇ
13435                   struct Row4;
13436                   ˇstruct Row5;
13437                   struct Row6;
13438                   ˇ
13439                   ˇstruct Row8;
13440                   struct Row9;
13441                   ˇstruct Row10;"#},
13442        base_text,
13443        &mut cx,
13444    );
13445}
13446
13447#[gpui::test]
13448async fn test_modification_reverts(cx: &mut TestAppContext) {
13449    init_test(cx, |_| {});
13450    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13451    let base_text = indoc! {r#"
13452        struct Row;
13453        struct Row1;
13454        struct Row2;
13455
13456        struct Row4;
13457        struct Row5;
13458        struct Row6;
13459
13460        struct Row8;
13461        struct Row9;
13462        struct Row10;"#};
13463
13464    // Modification hunks behave the same as the addition ones.
13465    assert_hunk_revert(
13466        indoc! {r#"struct Row;
13467                   struct Row1;
13468                   struct Row33;
13469                   ˇ
13470                   struct Row4;
13471                   struct Row5;
13472                   struct Row6;
13473                   ˇ
13474                   struct Row99;
13475                   struct Row9;
13476                   struct Row10;"#},
13477        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13478        indoc! {r#"struct Row;
13479                   struct Row1;
13480                   struct Row33;
13481                   ˇ
13482                   struct Row4;
13483                   struct Row5;
13484                   struct Row6;
13485                   ˇ
13486                   struct Row99;
13487                   struct Row9;
13488                   struct Row10;"#},
13489        base_text,
13490        &mut cx,
13491    );
13492    assert_hunk_revert(
13493        indoc! {r#"struct Row;
13494                   struct Row1;
13495                   struct Row33;
13496                   «ˇ
13497                   struct Row4;
13498                   struct» Row5;
13499                   «struct Row6;
13500                   ˇ»
13501                   struct Row99;
13502                   struct Row9;
13503                   struct Row10;"#},
13504        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13505        indoc! {r#"struct Row;
13506                   struct Row1;
13507                   struct Row33;
13508                   «ˇ
13509                   struct Row4;
13510                   struct» Row5;
13511                   «struct Row6;
13512                   ˇ»
13513                   struct Row99;
13514                   struct Row9;
13515                   struct Row10;"#},
13516        base_text,
13517        &mut cx,
13518    );
13519
13520    assert_hunk_revert(
13521        indoc! {r#"ˇstruct Row1.1;
13522                   struct Row1;
13523                   «ˇstr»uct Row22;
13524
13525                   struct ˇRow44;
13526                   struct Row5;
13527                   struct «Rˇ»ow66;ˇ
13528
13529                   «struˇ»ct Row88;
13530                   struct Row9;
13531                   struct Row1011;ˇ"#},
13532        vec![
13533            DiffHunkStatusKind::Modified,
13534            DiffHunkStatusKind::Modified,
13535            DiffHunkStatusKind::Modified,
13536            DiffHunkStatusKind::Modified,
13537            DiffHunkStatusKind::Modified,
13538            DiffHunkStatusKind::Modified,
13539        ],
13540        indoc! {r#"struct Row;
13541                   ˇstruct Row1;
13542                   struct Row2;
13543                   ˇ
13544                   struct Row4;
13545                   ˇstruct Row5;
13546                   struct Row6;
13547                   ˇ
13548                   struct Row8;
13549                   ˇstruct Row9;
13550                   struct Row10;ˇ"#},
13551        base_text,
13552        &mut cx,
13553    );
13554}
13555
13556#[gpui::test]
13557async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13558    init_test(cx, |_| {});
13559    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13560    let base_text = indoc! {r#"
13561        one
13562
13563        two
13564        three
13565        "#};
13566
13567    cx.set_head_text(base_text);
13568    cx.set_state("\nˇ\n");
13569    cx.executor().run_until_parked();
13570    cx.update_editor(|editor, _window, cx| {
13571        editor.expand_selected_diff_hunks(cx);
13572    });
13573    cx.executor().run_until_parked();
13574    cx.update_editor(|editor, window, cx| {
13575        editor.backspace(&Default::default(), window, cx);
13576    });
13577    cx.run_until_parked();
13578    cx.assert_state_with_diff(
13579        indoc! {r#"
13580
13581        - two
13582        - threeˇ
13583        +
13584        "#}
13585        .to_string(),
13586    );
13587}
13588
13589#[gpui::test]
13590async fn test_deletion_reverts(cx: &mut TestAppContext) {
13591    init_test(cx, |_| {});
13592    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13593    let base_text = indoc! {r#"struct Row;
13594struct Row1;
13595struct Row2;
13596
13597struct Row4;
13598struct Row5;
13599struct Row6;
13600
13601struct Row8;
13602struct Row9;
13603struct Row10;"#};
13604
13605    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13606    assert_hunk_revert(
13607        indoc! {r#"struct Row;
13608                   struct Row2;
13609
13610                   ˇstruct Row4;
13611                   struct Row5;
13612                   struct Row6;
13613                   ˇ
13614                   struct Row8;
13615                   struct Row10;"#},
13616        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13617        indoc! {r#"struct Row;
13618                   struct Row2;
13619
13620                   ˇstruct Row4;
13621                   struct Row5;
13622                   struct Row6;
13623                   ˇ
13624                   struct Row8;
13625                   struct Row10;"#},
13626        base_text,
13627        &mut cx,
13628    );
13629    assert_hunk_revert(
13630        indoc! {r#"struct Row;
13631                   struct Row2;
13632
13633                   «ˇstruct Row4;
13634                   struct» Row5;
13635                   «struct Row6;
13636                   ˇ»
13637                   struct Row8;
13638                   struct Row10;"#},
13639        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13640        indoc! {r#"struct Row;
13641                   struct Row2;
13642
13643                   «ˇstruct Row4;
13644                   struct» Row5;
13645                   «struct Row6;
13646                   ˇ»
13647                   struct Row8;
13648                   struct Row10;"#},
13649        base_text,
13650        &mut cx,
13651    );
13652
13653    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13654    assert_hunk_revert(
13655        indoc! {r#"struct Row;
13656                   ˇstruct Row2;
13657
13658                   struct Row4;
13659                   struct Row5;
13660                   struct Row6;
13661
13662                   struct Row8;ˇ
13663                   struct Row10;"#},
13664        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13665        indoc! {r#"struct Row;
13666                   struct Row1;
13667                   ˇstruct Row2;
13668
13669                   struct Row4;
13670                   struct Row5;
13671                   struct Row6;
13672
13673                   struct Row8;ˇ
13674                   struct Row9;
13675                   struct Row10;"#},
13676        base_text,
13677        &mut cx,
13678    );
13679    assert_hunk_revert(
13680        indoc! {r#"struct Row;
13681                   struct Row2«ˇ;
13682                   struct Row4;
13683                   struct» Row5;
13684                   «struct Row6;
13685
13686                   struct Row8;ˇ»
13687                   struct Row10;"#},
13688        vec![
13689            DiffHunkStatusKind::Deleted,
13690            DiffHunkStatusKind::Deleted,
13691            DiffHunkStatusKind::Deleted,
13692        ],
13693        indoc! {r#"struct Row;
13694                   struct Row1;
13695                   struct Row2«ˇ;
13696
13697                   struct Row4;
13698                   struct» Row5;
13699                   «struct Row6;
13700
13701                   struct Row8;ˇ»
13702                   struct Row9;
13703                   struct Row10;"#},
13704        base_text,
13705        &mut cx,
13706    );
13707}
13708
13709#[gpui::test]
13710async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13711    init_test(cx, |_| {});
13712
13713    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13714    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13715    let base_text_3 =
13716        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13717
13718    let text_1 = edit_first_char_of_every_line(base_text_1);
13719    let text_2 = edit_first_char_of_every_line(base_text_2);
13720    let text_3 = edit_first_char_of_every_line(base_text_3);
13721
13722    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13723    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13724    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13725
13726    let multibuffer = cx.new(|cx| {
13727        let mut multibuffer = MultiBuffer::new(ReadWrite);
13728        multibuffer.push_excerpts(
13729            buffer_1.clone(),
13730            [
13731                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13732                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13733                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13734            ],
13735            cx,
13736        );
13737        multibuffer.push_excerpts(
13738            buffer_2.clone(),
13739            [
13740                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13741                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13742                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13743            ],
13744            cx,
13745        );
13746        multibuffer.push_excerpts(
13747            buffer_3.clone(),
13748            [
13749                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13750                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13751                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13752            ],
13753            cx,
13754        );
13755        multibuffer
13756    });
13757
13758    let fs = FakeFs::new(cx.executor());
13759    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13760    let (editor, cx) = cx
13761        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13762    editor.update_in(cx, |editor, _window, cx| {
13763        for (buffer, diff_base) in [
13764            (buffer_1.clone(), base_text_1),
13765            (buffer_2.clone(), base_text_2),
13766            (buffer_3.clone(), base_text_3),
13767        ] {
13768            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13769            editor
13770                .buffer
13771                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13772        }
13773    });
13774    cx.executor().run_until_parked();
13775
13776    editor.update_in(cx, |editor, window, cx| {
13777        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}");
13778        editor.select_all(&SelectAll, window, cx);
13779        editor.git_restore(&Default::default(), window, cx);
13780    });
13781    cx.executor().run_until_parked();
13782
13783    // When all ranges are selected, all buffer hunks are reverted.
13784    editor.update(cx, |editor, cx| {
13785        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");
13786    });
13787    buffer_1.update(cx, |buffer, _| {
13788        assert_eq!(buffer.text(), base_text_1);
13789    });
13790    buffer_2.update(cx, |buffer, _| {
13791        assert_eq!(buffer.text(), base_text_2);
13792    });
13793    buffer_3.update(cx, |buffer, _| {
13794        assert_eq!(buffer.text(), base_text_3);
13795    });
13796
13797    editor.update_in(cx, |editor, window, cx| {
13798        editor.undo(&Default::default(), window, cx);
13799    });
13800
13801    editor.update_in(cx, |editor, window, cx| {
13802        editor.change_selections(None, window, cx, |s| {
13803            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13804        });
13805        editor.git_restore(&Default::default(), window, cx);
13806    });
13807
13808    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13809    // but not affect buffer_2 and its related excerpts.
13810    editor.update(cx, |editor, cx| {
13811        assert_eq!(
13812            editor.text(cx),
13813            "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}"
13814        );
13815    });
13816    buffer_1.update(cx, |buffer, _| {
13817        assert_eq!(buffer.text(), base_text_1);
13818    });
13819    buffer_2.update(cx, |buffer, _| {
13820        assert_eq!(
13821            buffer.text(),
13822            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13823        );
13824    });
13825    buffer_3.update(cx, |buffer, _| {
13826        assert_eq!(
13827            buffer.text(),
13828            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13829        );
13830    });
13831
13832    fn edit_first_char_of_every_line(text: &str) -> String {
13833        text.split('\n')
13834            .map(|line| format!("X{}", &line[1..]))
13835            .collect::<Vec<_>>()
13836            .join("\n")
13837    }
13838}
13839
13840#[gpui::test]
13841async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13842    init_test(cx, |_| {});
13843
13844    let cols = 4;
13845    let rows = 10;
13846    let sample_text_1 = sample_text(rows, cols, 'a');
13847    assert_eq!(
13848        sample_text_1,
13849        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13850    );
13851    let sample_text_2 = sample_text(rows, cols, 'l');
13852    assert_eq!(
13853        sample_text_2,
13854        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13855    );
13856    let sample_text_3 = sample_text(rows, cols, 'v');
13857    assert_eq!(
13858        sample_text_3,
13859        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13860    );
13861
13862    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13863    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13864    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13865
13866    let multi_buffer = cx.new(|cx| {
13867        let mut multibuffer = MultiBuffer::new(ReadWrite);
13868        multibuffer.push_excerpts(
13869            buffer_1.clone(),
13870            [
13871                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13872                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13873                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13874            ],
13875            cx,
13876        );
13877        multibuffer.push_excerpts(
13878            buffer_2.clone(),
13879            [
13880                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13881                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13882                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13883            ],
13884            cx,
13885        );
13886        multibuffer.push_excerpts(
13887            buffer_3.clone(),
13888            [
13889                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13890                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13891                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13892            ],
13893            cx,
13894        );
13895        multibuffer
13896    });
13897
13898    let fs = FakeFs::new(cx.executor());
13899    fs.insert_tree(
13900        "/a",
13901        json!({
13902            "main.rs": sample_text_1,
13903            "other.rs": sample_text_2,
13904            "lib.rs": sample_text_3,
13905        }),
13906    )
13907    .await;
13908    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13909    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13910    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13911    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13912        Editor::new(
13913            EditorMode::Full,
13914            multi_buffer,
13915            Some(project.clone()),
13916            window,
13917            cx,
13918        )
13919    });
13920    let multibuffer_item_id = workspace
13921        .update(cx, |workspace, window, cx| {
13922            assert!(
13923                workspace.active_item(cx).is_none(),
13924                "active item should be None before the first item is added"
13925            );
13926            workspace.add_item_to_active_pane(
13927                Box::new(multi_buffer_editor.clone()),
13928                None,
13929                true,
13930                window,
13931                cx,
13932            );
13933            let active_item = workspace
13934                .active_item(cx)
13935                .expect("should have an active item after adding the multi buffer");
13936            assert!(
13937                !active_item.is_singleton(cx),
13938                "A multi buffer was expected to active after adding"
13939            );
13940            active_item.item_id()
13941        })
13942        .unwrap();
13943    cx.executor().run_until_parked();
13944
13945    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13946        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13947            s.select_ranges(Some(1..2))
13948        });
13949        editor.open_excerpts(&OpenExcerpts, window, cx);
13950    });
13951    cx.executor().run_until_parked();
13952    let first_item_id = workspace
13953        .update(cx, |workspace, window, cx| {
13954            let active_item = workspace
13955                .active_item(cx)
13956                .expect("should have an active item after navigating into the 1st buffer");
13957            let first_item_id = active_item.item_id();
13958            assert_ne!(
13959                first_item_id, multibuffer_item_id,
13960                "Should navigate into the 1st buffer and activate it"
13961            );
13962            assert!(
13963                active_item.is_singleton(cx),
13964                "New active item should be a singleton buffer"
13965            );
13966            assert_eq!(
13967                active_item
13968                    .act_as::<Editor>(cx)
13969                    .expect("should have navigated into an editor for the 1st buffer")
13970                    .read(cx)
13971                    .text(cx),
13972                sample_text_1
13973            );
13974
13975            workspace
13976                .go_back(workspace.active_pane().downgrade(), window, cx)
13977                .detach_and_log_err(cx);
13978
13979            first_item_id
13980        })
13981        .unwrap();
13982    cx.executor().run_until_parked();
13983    workspace
13984        .update(cx, |workspace, _, cx| {
13985            let active_item = workspace
13986                .active_item(cx)
13987                .expect("should have an active item after navigating back");
13988            assert_eq!(
13989                active_item.item_id(),
13990                multibuffer_item_id,
13991                "Should navigate back to the multi buffer"
13992            );
13993            assert!(!active_item.is_singleton(cx));
13994        })
13995        .unwrap();
13996
13997    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13998        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13999            s.select_ranges(Some(39..40))
14000        });
14001        editor.open_excerpts(&OpenExcerpts, window, cx);
14002    });
14003    cx.executor().run_until_parked();
14004    let second_item_id = workspace
14005        .update(cx, |workspace, window, cx| {
14006            let active_item = workspace
14007                .active_item(cx)
14008                .expect("should have an active item after navigating into the 2nd buffer");
14009            let second_item_id = active_item.item_id();
14010            assert_ne!(
14011                second_item_id, multibuffer_item_id,
14012                "Should navigate away from the multibuffer"
14013            );
14014            assert_ne!(
14015                second_item_id, first_item_id,
14016                "Should navigate into the 2nd buffer and activate it"
14017            );
14018            assert!(
14019                active_item.is_singleton(cx),
14020                "New active item should be a singleton buffer"
14021            );
14022            assert_eq!(
14023                active_item
14024                    .act_as::<Editor>(cx)
14025                    .expect("should have navigated into an editor")
14026                    .read(cx)
14027                    .text(cx),
14028                sample_text_2
14029            );
14030
14031            workspace
14032                .go_back(workspace.active_pane().downgrade(), window, cx)
14033                .detach_and_log_err(cx);
14034
14035            second_item_id
14036        })
14037        .unwrap();
14038    cx.executor().run_until_parked();
14039    workspace
14040        .update(cx, |workspace, _, cx| {
14041            let active_item = workspace
14042                .active_item(cx)
14043                .expect("should have an active item after navigating back from the 2nd buffer");
14044            assert_eq!(
14045                active_item.item_id(),
14046                multibuffer_item_id,
14047                "Should navigate back from the 2nd buffer to the multi buffer"
14048            );
14049            assert!(!active_item.is_singleton(cx));
14050        })
14051        .unwrap();
14052
14053    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14054        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14055            s.select_ranges(Some(70..70))
14056        });
14057        editor.open_excerpts(&OpenExcerpts, window, cx);
14058    });
14059    cx.executor().run_until_parked();
14060    workspace
14061        .update(cx, |workspace, window, cx| {
14062            let active_item = workspace
14063                .active_item(cx)
14064                .expect("should have an active item after navigating into the 3rd buffer");
14065            let third_item_id = active_item.item_id();
14066            assert_ne!(
14067                third_item_id, multibuffer_item_id,
14068                "Should navigate into the 3rd buffer and activate it"
14069            );
14070            assert_ne!(third_item_id, first_item_id);
14071            assert_ne!(third_item_id, second_item_id);
14072            assert!(
14073                active_item.is_singleton(cx),
14074                "New active item should be a singleton buffer"
14075            );
14076            assert_eq!(
14077                active_item
14078                    .act_as::<Editor>(cx)
14079                    .expect("should have navigated into an editor")
14080                    .read(cx)
14081                    .text(cx),
14082                sample_text_3
14083            );
14084
14085            workspace
14086                .go_back(workspace.active_pane().downgrade(), window, cx)
14087                .detach_and_log_err(cx);
14088        })
14089        .unwrap();
14090    cx.executor().run_until_parked();
14091    workspace
14092        .update(cx, |workspace, _, cx| {
14093            let active_item = workspace
14094                .active_item(cx)
14095                .expect("should have an active item after navigating back from the 3rd buffer");
14096            assert_eq!(
14097                active_item.item_id(),
14098                multibuffer_item_id,
14099                "Should navigate back from the 3rd buffer to the multi buffer"
14100            );
14101            assert!(!active_item.is_singleton(cx));
14102        })
14103        .unwrap();
14104}
14105
14106#[gpui::test]
14107async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14108    init_test(cx, |_| {});
14109
14110    let mut cx = EditorTestContext::new(cx).await;
14111
14112    let diff_base = r#"
14113        use some::mod;
14114
14115        const A: u32 = 42;
14116
14117        fn main() {
14118            println!("hello");
14119
14120            println!("world");
14121        }
14122        "#
14123    .unindent();
14124
14125    cx.set_state(
14126        &r#"
14127        use some::modified;
14128
14129        ˇ
14130        fn main() {
14131            println!("hello there");
14132
14133            println!("around the");
14134            println!("world");
14135        }
14136        "#
14137        .unindent(),
14138    );
14139
14140    cx.set_head_text(&diff_base);
14141    executor.run_until_parked();
14142
14143    cx.update_editor(|editor, window, cx| {
14144        editor.go_to_next_hunk(&GoToHunk, window, cx);
14145        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14146    });
14147    executor.run_until_parked();
14148    cx.assert_state_with_diff(
14149        r#"
14150          use some::modified;
14151
14152
14153          fn main() {
14154        -     println!("hello");
14155        + ˇ    println!("hello there");
14156
14157              println!("around the");
14158              println!("world");
14159          }
14160        "#
14161        .unindent(),
14162    );
14163
14164    cx.update_editor(|editor, window, cx| {
14165        for _ in 0..2 {
14166            editor.go_to_next_hunk(&GoToHunk, window, cx);
14167            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14168        }
14169    });
14170    executor.run_until_parked();
14171    cx.assert_state_with_diff(
14172        r#"
14173        - use some::mod;
14174        + ˇuse some::modified;
14175
14176
14177          fn main() {
14178        -     println!("hello");
14179        +     println!("hello there");
14180
14181        +     println!("around the");
14182              println!("world");
14183          }
14184        "#
14185        .unindent(),
14186    );
14187
14188    cx.update_editor(|editor, window, cx| {
14189        editor.go_to_next_hunk(&GoToHunk, window, cx);
14190        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14191    });
14192    executor.run_until_parked();
14193    cx.assert_state_with_diff(
14194        r#"
14195        - use some::mod;
14196        + use some::modified;
14197
14198        - const A: u32 = 42;
14199          ˇ
14200          fn main() {
14201        -     println!("hello");
14202        +     println!("hello there");
14203
14204        +     println!("around the");
14205              println!("world");
14206          }
14207        "#
14208        .unindent(),
14209    );
14210
14211    cx.update_editor(|editor, window, cx| {
14212        editor.cancel(&Cancel, window, cx);
14213    });
14214
14215    cx.assert_state_with_diff(
14216        r#"
14217          use some::modified;
14218
14219          ˇ
14220          fn main() {
14221              println!("hello there");
14222
14223              println!("around the");
14224              println!("world");
14225          }
14226        "#
14227        .unindent(),
14228    );
14229}
14230
14231#[gpui::test]
14232async fn test_diff_base_change_with_expanded_diff_hunks(
14233    executor: BackgroundExecutor,
14234    cx: &mut TestAppContext,
14235) {
14236    init_test(cx, |_| {});
14237
14238    let mut cx = EditorTestContext::new(cx).await;
14239
14240    let diff_base = r#"
14241        use some::mod1;
14242        use some::mod2;
14243
14244        const A: u32 = 42;
14245        const B: u32 = 42;
14246        const C: u32 = 42;
14247
14248        fn main() {
14249            println!("hello");
14250
14251            println!("world");
14252        }
14253        "#
14254    .unindent();
14255
14256    cx.set_state(
14257        &r#"
14258        use some::mod2;
14259
14260        const A: u32 = 42;
14261        const C: u32 = 42;
14262
14263        fn main(ˇ) {
14264            //println!("hello");
14265
14266            println!("world");
14267            //
14268            //
14269        }
14270        "#
14271        .unindent(),
14272    );
14273
14274    cx.set_head_text(&diff_base);
14275    executor.run_until_parked();
14276
14277    cx.update_editor(|editor, window, cx| {
14278        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14279    });
14280    executor.run_until_parked();
14281    cx.assert_state_with_diff(
14282        r#"
14283        - use some::mod1;
14284          use some::mod2;
14285
14286          const A: u32 = 42;
14287        - const B: u32 = 42;
14288          const C: u32 = 42;
14289
14290          fn main(ˇ) {
14291        -     println!("hello");
14292        +     //println!("hello");
14293
14294              println!("world");
14295        +     //
14296        +     //
14297          }
14298        "#
14299        .unindent(),
14300    );
14301
14302    cx.set_head_text("new diff base!");
14303    executor.run_until_parked();
14304    cx.assert_state_with_diff(
14305        r#"
14306        - new diff base!
14307        + use some::mod2;
14308        +
14309        + const A: u32 = 42;
14310        + const C: u32 = 42;
14311        +
14312        + fn main(ˇ) {
14313        +     //println!("hello");
14314        +
14315        +     println!("world");
14316        +     //
14317        +     //
14318        + }
14319        "#
14320        .unindent(),
14321    );
14322}
14323
14324#[gpui::test]
14325async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14326    init_test(cx, |_| {});
14327
14328    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14329    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14330    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14331    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14332    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14333    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14334
14335    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14336    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14337    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14338
14339    let multi_buffer = cx.new(|cx| {
14340        let mut multibuffer = MultiBuffer::new(ReadWrite);
14341        multibuffer.push_excerpts(
14342            buffer_1.clone(),
14343            [
14344                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14345                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14346                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14347            ],
14348            cx,
14349        );
14350        multibuffer.push_excerpts(
14351            buffer_2.clone(),
14352            [
14353                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14354                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14355                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14356            ],
14357            cx,
14358        );
14359        multibuffer.push_excerpts(
14360            buffer_3.clone(),
14361            [
14362                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14363                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14364                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14365            ],
14366            cx,
14367        );
14368        multibuffer
14369    });
14370
14371    let editor =
14372        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14373    editor
14374        .update(cx, |editor, _window, cx| {
14375            for (buffer, diff_base) in [
14376                (buffer_1.clone(), file_1_old),
14377                (buffer_2.clone(), file_2_old),
14378                (buffer_3.clone(), file_3_old),
14379            ] {
14380                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14381                editor
14382                    .buffer
14383                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14384            }
14385        })
14386        .unwrap();
14387
14388    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14389    cx.run_until_parked();
14390
14391    cx.assert_editor_state(
14392        &"
14393            ˇaaa
14394            ccc
14395            ddd
14396
14397            ggg
14398            hhh
14399
14400
14401            lll
14402            mmm
14403            NNN
14404
14405            qqq
14406            rrr
14407
14408            uuu
14409            111
14410            222
14411            333
14412
14413            666
14414            777
14415
14416            000
14417            !!!"
14418        .unindent(),
14419    );
14420
14421    cx.update_editor(|editor, window, cx| {
14422        editor.select_all(&SelectAll, window, cx);
14423        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14424    });
14425    cx.executor().run_until_parked();
14426
14427    cx.assert_state_with_diff(
14428        "
14429            «aaa
14430          - bbb
14431            ccc
14432            ddd
14433
14434            ggg
14435            hhh
14436
14437
14438            lll
14439            mmm
14440          - nnn
14441          + NNN
14442
14443            qqq
14444            rrr
14445
14446            uuu
14447            111
14448            222
14449            333
14450
14451          + 666
14452            777
14453
14454            000
14455            !!!ˇ»"
14456            .unindent(),
14457    );
14458}
14459
14460#[gpui::test]
14461async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14462    init_test(cx, |_| {});
14463
14464    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14465    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14466
14467    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14468    let multi_buffer = cx.new(|cx| {
14469        let mut multibuffer = MultiBuffer::new(ReadWrite);
14470        multibuffer.push_excerpts(
14471            buffer.clone(),
14472            [
14473                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14474                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14475                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14476            ],
14477            cx,
14478        );
14479        multibuffer
14480    });
14481
14482    let editor =
14483        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14484    editor
14485        .update(cx, |editor, _window, cx| {
14486            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14487            editor
14488                .buffer
14489                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14490        })
14491        .unwrap();
14492
14493    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14494    cx.run_until_parked();
14495
14496    cx.update_editor(|editor, window, cx| {
14497        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14498    });
14499    cx.executor().run_until_parked();
14500
14501    // When the start of a hunk coincides with the start of its excerpt,
14502    // the hunk is expanded. When the start of a a hunk is earlier than
14503    // the start of its excerpt, the hunk is not expanded.
14504    cx.assert_state_with_diff(
14505        "
14506            ˇaaa
14507          - bbb
14508          + BBB
14509
14510          - ddd
14511          - eee
14512          + DDD
14513          + EEE
14514            fff
14515
14516            iii
14517        "
14518        .unindent(),
14519    );
14520}
14521
14522#[gpui::test]
14523async fn test_edits_around_expanded_insertion_hunks(
14524    executor: BackgroundExecutor,
14525    cx: &mut TestAppContext,
14526) {
14527    init_test(cx, |_| {});
14528
14529    let mut cx = EditorTestContext::new(cx).await;
14530
14531    let diff_base = r#"
14532        use some::mod1;
14533        use some::mod2;
14534
14535        const A: u32 = 42;
14536
14537        fn main() {
14538            println!("hello");
14539
14540            println!("world");
14541        }
14542        "#
14543    .unindent();
14544    executor.run_until_parked();
14545    cx.set_state(
14546        &r#"
14547        use some::mod1;
14548        use some::mod2;
14549
14550        const A: u32 = 42;
14551        const B: u32 = 42;
14552        const C: u32 = 42;
14553        ˇ
14554
14555        fn main() {
14556            println!("hello");
14557
14558            println!("world");
14559        }
14560        "#
14561        .unindent(),
14562    );
14563
14564    cx.set_head_text(&diff_base);
14565    executor.run_until_parked();
14566
14567    cx.update_editor(|editor, window, cx| {
14568        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14569    });
14570    executor.run_until_parked();
14571
14572    cx.assert_state_with_diff(
14573        r#"
14574        use some::mod1;
14575        use some::mod2;
14576
14577        const A: u32 = 42;
14578      + const B: u32 = 42;
14579      + const C: u32 = 42;
14580      + ˇ
14581
14582        fn main() {
14583            println!("hello");
14584
14585            println!("world");
14586        }
14587      "#
14588        .unindent(),
14589    );
14590
14591    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14592    executor.run_until_parked();
14593
14594    cx.assert_state_with_diff(
14595        r#"
14596        use some::mod1;
14597        use some::mod2;
14598
14599        const A: u32 = 42;
14600      + const B: u32 = 42;
14601      + const C: u32 = 42;
14602      + const D: u32 = 42;
14603      + ˇ
14604
14605        fn main() {
14606            println!("hello");
14607
14608            println!("world");
14609        }
14610      "#
14611        .unindent(),
14612    );
14613
14614    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14615    executor.run_until_parked();
14616
14617    cx.assert_state_with_diff(
14618        r#"
14619        use some::mod1;
14620        use some::mod2;
14621
14622        const A: u32 = 42;
14623      + const B: u32 = 42;
14624      + const C: u32 = 42;
14625      + const D: u32 = 42;
14626      + const E: u32 = 42;
14627      + ˇ
14628
14629        fn main() {
14630            println!("hello");
14631
14632            println!("world");
14633        }
14634      "#
14635        .unindent(),
14636    );
14637
14638    cx.update_editor(|editor, window, cx| {
14639        editor.delete_line(&DeleteLine, window, cx);
14640    });
14641    executor.run_until_parked();
14642
14643    cx.assert_state_with_diff(
14644        r#"
14645        use some::mod1;
14646        use some::mod2;
14647
14648        const A: u32 = 42;
14649      + const B: u32 = 42;
14650      + const C: u32 = 42;
14651      + const D: u32 = 42;
14652      + const E: u32 = 42;
14653        ˇ
14654        fn main() {
14655            println!("hello");
14656
14657            println!("world");
14658        }
14659      "#
14660        .unindent(),
14661    );
14662
14663    cx.update_editor(|editor, window, cx| {
14664        editor.move_up(&MoveUp, window, cx);
14665        editor.delete_line(&DeleteLine, window, cx);
14666        editor.move_up(&MoveUp, window, cx);
14667        editor.delete_line(&DeleteLine, window, cx);
14668        editor.move_up(&MoveUp, window, cx);
14669        editor.delete_line(&DeleteLine, window, cx);
14670    });
14671    executor.run_until_parked();
14672    cx.assert_state_with_diff(
14673        r#"
14674        use some::mod1;
14675        use some::mod2;
14676
14677        const A: u32 = 42;
14678      + const B: u32 = 42;
14679        ˇ
14680        fn main() {
14681            println!("hello");
14682
14683            println!("world");
14684        }
14685      "#
14686        .unindent(),
14687    );
14688
14689    cx.update_editor(|editor, window, cx| {
14690        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14691        editor.delete_line(&DeleteLine, window, cx);
14692    });
14693    executor.run_until_parked();
14694    cx.assert_state_with_diff(
14695        r#"
14696        ˇ
14697        fn main() {
14698            println!("hello");
14699
14700            println!("world");
14701        }
14702      "#
14703        .unindent(),
14704    );
14705}
14706
14707#[gpui::test]
14708async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14709    init_test(cx, |_| {});
14710
14711    let mut cx = EditorTestContext::new(cx).await;
14712    cx.set_head_text(indoc! { "
14713        one
14714        two
14715        three
14716        four
14717        five
14718        "
14719    });
14720    cx.set_state(indoc! { "
14721        one
14722        ˇthree
14723        five
14724    "});
14725    cx.run_until_parked();
14726    cx.update_editor(|editor, window, cx| {
14727        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14728    });
14729    cx.assert_state_with_diff(
14730        indoc! { "
14731        one
14732      - two
14733        ˇthree
14734      - four
14735        five
14736    "}
14737        .to_string(),
14738    );
14739    cx.update_editor(|editor, window, cx| {
14740        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14741    });
14742
14743    cx.assert_state_with_diff(
14744        indoc! { "
14745        one
14746        ˇthree
14747        five
14748    "}
14749        .to_string(),
14750    );
14751
14752    cx.set_state(indoc! { "
14753        one
14754        ˇTWO
14755        three
14756        four
14757        five
14758    "});
14759    cx.run_until_parked();
14760    cx.update_editor(|editor, window, cx| {
14761        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14762    });
14763
14764    cx.assert_state_with_diff(
14765        indoc! { "
14766            one
14767          - two
14768          + ˇTWO
14769            three
14770            four
14771            five
14772        "}
14773        .to_string(),
14774    );
14775    cx.update_editor(|editor, window, cx| {
14776        editor.move_up(&Default::default(), window, cx);
14777        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14778    });
14779    cx.assert_state_with_diff(
14780        indoc! { "
14781            one
14782            ˇTWO
14783            three
14784            four
14785            five
14786        "}
14787        .to_string(),
14788    );
14789}
14790
14791#[gpui::test]
14792async fn test_edits_around_expanded_deletion_hunks(
14793    executor: BackgroundExecutor,
14794    cx: &mut TestAppContext,
14795) {
14796    init_test(cx, |_| {});
14797
14798    let mut cx = EditorTestContext::new(cx).await;
14799
14800    let diff_base = r#"
14801        use some::mod1;
14802        use some::mod2;
14803
14804        const A: u32 = 42;
14805        const B: u32 = 42;
14806        const C: u32 = 42;
14807
14808
14809        fn main() {
14810            println!("hello");
14811
14812            println!("world");
14813        }
14814    "#
14815    .unindent();
14816    executor.run_until_parked();
14817    cx.set_state(
14818        &r#"
14819        use some::mod1;
14820        use some::mod2;
14821
14822        ˇconst B: u32 = 42;
14823        const C: u32 = 42;
14824
14825
14826        fn main() {
14827            println!("hello");
14828
14829            println!("world");
14830        }
14831        "#
14832        .unindent(),
14833    );
14834
14835    cx.set_head_text(&diff_base);
14836    executor.run_until_parked();
14837
14838    cx.update_editor(|editor, window, cx| {
14839        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14840    });
14841    executor.run_until_parked();
14842
14843    cx.assert_state_with_diff(
14844        r#"
14845        use some::mod1;
14846        use some::mod2;
14847
14848      - const A: u32 = 42;
14849        ˇconst B: u32 = 42;
14850        const C: u32 = 42;
14851
14852
14853        fn main() {
14854            println!("hello");
14855
14856            println!("world");
14857        }
14858      "#
14859        .unindent(),
14860    );
14861
14862    cx.update_editor(|editor, window, cx| {
14863        editor.delete_line(&DeleteLine, window, cx);
14864    });
14865    executor.run_until_parked();
14866    cx.assert_state_with_diff(
14867        r#"
14868        use some::mod1;
14869        use some::mod2;
14870
14871      - const A: u32 = 42;
14872      - const B: u32 = 42;
14873        ˇconst C: u32 = 42;
14874
14875
14876        fn main() {
14877            println!("hello");
14878
14879            println!("world");
14880        }
14881      "#
14882        .unindent(),
14883    );
14884
14885    cx.update_editor(|editor, window, cx| {
14886        editor.delete_line(&DeleteLine, window, cx);
14887    });
14888    executor.run_until_parked();
14889    cx.assert_state_with_diff(
14890        r#"
14891        use some::mod1;
14892        use some::mod2;
14893
14894      - const A: u32 = 42;
14895      - const B: u32 = 42;
14896      - const C: u32 = 42;
14897        ˇ
14898
14899        fn main() {
14900            println!("hello");
14901
14902            println!("world");
14903        }
14904      "#
14905        .unindent(),
14906    );
14907
14908    cx.update_editor(|editor, window, cx| {
14909        editor.handle_input("replacement", window, cx);
14910    });
14911    executor.run_until_parked();
14912    cx.assert_state_with_diff(
14913        r#"
14914        use some::mod1;
14915        use some::mod2;
14916
14917      - const A: u32 = 42;
14918      - const B: u32 = 42;
14919      - const C: u32 = 42;
14920      -
14921      + replacementˇ
14922
14923        fn main() {
14924            println!("hello");
14925
14926            println!("world");
14927        }
14928      "#
14929        .unindent(),
14930    );
14931}
14932
14933#[gpui::test]
14934async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14935    init_test(cx, |_| {});
14936
14937    let mut cx = EditorTestContext::new(cx).await;
14938
14939    let base_text = r#"
14940        one
14941        two
14942        three
14943        four
14944        five
14945    "#
14946    .unindent();
14947    executor.run_until_parked();
14948    cx.set_state(
14949        &r#"
14950        one
14951        two
14952        fˇour
14953        five
14954        "#
14955        .unindent(),
14956    );
14957
14958    cx.set_head_text(&base_text);
14959    executor.run_until_parked();
14960
14961    cx.update_editor(|editor, window, cx| {
14962        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14963    });
14964    executor.run_until_parked();
14965
14966    cx.assert_state_with_diff(
14967        r#"
14968          one
14969          two
14970        - three
14971          fˇour
14972          five
14973        "#
14974        .unindent(),
14975    );
14976
14977    cx.update_editor(|editor, window, cx| {
14978        editor.backspace(&Backspace, window, cx);
14979        editor.backspace(&Backspace, window, cx);
14980    });
14981    executor.run_until_parked();
14982    cx.assert_state_with_diff(
14983        r#"
14984          one
14985          two
14986        - threeˇ
14987        - four
14988        + our
14989          five
14990        "#
14991        .unindent(),
14992    );
14993}
14994
14995#[gpui::test]
14996async fn test_edit_after_expanded_modification_hunk(
14997    executor: BackgroundExecutor,
14998    cx: &mut TestAppContext,
14999) {
15000    init_test(cx, |_| {});
15001
15002    let mut cx = EditorTestContext::new(cx).await;
15003
15004    let diff_base = r#"
15005        use some::mod1;
15006        use some::mod2;
15007
15008        const A: u32 = 42;
15009        const B: u32 = 42;
15010        const C: u32 = 42;
15011        const D: u32 = 42;
15012
15013
15014        fn main() {
15015            println!("hello");
15016
15017            println!("world");
15018        }"#
15019    .unindent();
15020
15021    cx.set_state(
15022        &r#"
15023        use some::mod1;
15024        use some::mod2;
15025
15026        const A: u32 = 42;
15027        const B: u32 = 42;
15028        const C: u32 = 43ˇ
15029        const D: u32 = 42;
15030
15031
15032        fn main() {
15033            println!("hello");
15034
15035            println!("world");
15036        }"#
15037        .unindent(),
15038    );
15039
15040    cx.set_head_text(&diff_base);
15041    executor.run_until_parked();
15042    cx.update_editor(|editor, window, cx| {
15043        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15044    });
15045    executor.run_until_parked();
15046
15047    cx.assert_state_with_diff(
15048        r#"
15049        use some::mod1;
15050        use some::mod2;
15051
15052        const A: u32 = 42;
15053        const B: u32 = 42;
15054      - const C: u32 = 42;
15055      + const C: u32 = 43ˇ
15056        const D: u32 = 42;
15057
15058
15059        fn main() {
15060            println!("hello");
15061
15062            println!("world");
15063        }"#
15064        .unindent(),
15065    );
15066
15067    cx.update_editor(|editor, window, cx| {
15068        editor.handle_input("\nnew_line\n", window, cx);
15069    });
15070    executor.run_until_parked();
15071
15072    cx.assert_state_with_diff(
15073        r#"
15074        use some::mod1;
15075        use some::mod2;
15076
15077        const A: u32 = 42;
15078        const B: u32 = 42;
15079      - const C: u32 = 42;
15080      + const C: u32 = 43
15081      + new_line
15082      + ˇ
15083        const D: u32 = 42;
15084
15085
15086        fn main() {
15087            println!("hello");
15088
15089            println!("world");
15090        }"#
15091        .unindent(),
15092    );
15093}
15094
15095#[gpui::test]
15096async fn test_stage_and_unstage_added_file_hunk(
15097    executor: BackgroundExecutor,
15098    cx: &mut TestAppContext,
15099) {
15100    init_test(cx, |_| {});
15101
15102    let mut cx = EditorTestContext::new(cx).await;
15103    cx.update_editor(|editor, _, cx| {
15104        editor.set_expand_all_diff_hunks(cx);
15105    });
15106
15107    let working_copy = r#"
15108            ˇfn main() {
15109                println!("hello, world!");
15110            }
15111        "#
15112    .unindent();
15113
15114    cx.set_state(&working_copy);
15115    executor.run_until_parked();
15116
15117    cx.assert_state_with_diff(
15118        r#"
15119            + ˇfn main() {
15120            +     println!("hello, world!");
15121            + }
15122        "#
15123        .unindent(),
15124    );
15125    cx.assert_index_text(None);
15126
15127    cx.update_editor(|editor, window, cx| {
15128        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15129    });
15130    executor.run_until_parked();
15131    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15132    cx.assert_state_with_diff(
15133        r#"
15134            + ˇfn main() {
15135            +     println!("hello, world!");
15136            + }
15137        "#
15138        .unindent(),
15139    );
15140
15141    cx.update_editor(|editor, window, cx| {
15142        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15143    });
15144    executor.run_until_parked();
15145    cx.assert_index_text(None);
15146}
15147
15148async fn setup_indent_guides_editor(
15149    text: &str,
15150    cx: &mut TestAppContext,
15151) -> (BufferId, EditorTestContext) {
15152    init_test(cx, |_| {});
15153
15154    let mut cx = EditorTestContext::new(cx).await;
15155
15156    let buffer_id = cx.update_editor(|editor, window, cx| {
15157        editor.set_text(text, window, cx);
15158        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15159
15160        buffer_ids[0]
15161    });
15162
15163    (buffer_id, cx)
15164}
15165
15166fn assert_indent_guides(
15167    range: Range<u32>,
15168    expected: Vec<IndentGuide>,
15169    active_indices: Option<Vec<usize>>,
15170    cx: &mut EditorTestContext,
15171) {
15172    let indent_guides = cx.update_editor(|editor, window, cx| {
15173        let snapshot = editor.snapshot(window, cx).display_snapshot;
15174        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15175            editor,
15176            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15177            true,
15178            &snapshot,
15179            cx,
15180        );
15181
15182        indent_guides.sort_by(|a, b| {
15183            a.depth.cmp(&b.depth).then(
15184                a.start_row
15185                    .cmp(&b.start_row)
15186                    .then(a.end_row.cmp(&b.end_row)),
15187            )
15188        });
15189        indent_guides
15190    });
15191
15192    if let Some(expected) = active_indices {
15193        let active_indices = cx.update_editor(|editor, window, cx| {
15194            let snapshot = editor.snapshot(window, cx).display_snapshot;
15195            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15196        });
15197
15198        assert_eq!(
15199            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15200            expected,
15201            "Active indent guide indices do not match"
15202        );
15203    }
15204
15205    assert_eq!(indent_guides, expected, "Indent guides do not match");
15206}
15207
15208fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15209    IndentGuide {
15210        buffer_id,
15211        start_row: MultiBufferRow(start_row),
15212        end_row: MultiBufferRow(end_row),
15213        depth,
15214        tab_size: 4,
15215        settings: IndentGuideSettings {
15216            enabled: true,
15217            line_width: 1,
15218            active_line_width: 1,
15219            ..Default::default()
15220        },
15221    }
15222}
15223
15224#[gpui::test]
15225async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15226    let (buffer_id, mut cx) = setup_indent_guides_editor(
15227        &"
15228    fn main() {
15229        let a = 1;
15230    }"
15231        .unindent(),
15232        cx,
15233    )
15234    .await;
15235
15236    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15237}
15238
15239#[gpui::test]
15240async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15241    let (buffer_id, mut cx) = setup_indent_guides_editor(
15242        &"
15243    fn main() {
15244        let a = 1;
15245        let b = 2;
15246    }"
15247        .unindent(),
15248        cx,
15249    )
15250    .await;
15251
15252    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15253}
15254
15255#[gpui::test]
15256async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15257    let (buffer_id, mut cx) = setup_indent_guides_editor(
15258        &"
15259    fn main() {
15260        let a = 1;
15261        if a == 3 {
15262            let b = 2;
15263        } else {
15264            let c = 3;
15265        }
15266    }"
15267        .unindent(),
15268        cx,
15269    )
15270    .await;
15271
15272    assert_indent_guides(
15273        0..8,
15274        vec![
15275            indent_guide(buffer_id, 1, 6, 0),
15276            indent_guide(buffer_id, 3, 3, 1),
15277            indent_guide(buffer_id, 5, 5, 1),
15278        ],
15279        None,
15280        &mut cx,
15281    );
15282}
15283
15284#[gpui::test]
15285async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15286    let (buffer_id, mut cx) = setup_indent_guides_editor(
15287        &"
15288    fn main() {
15289        let a = 1;
15290            let b = 2;
15291        let c = 3;
15292    }"
15293        .unindent(),
15294        cx,
15295    )
15296    .await;
15297
15298    assert_indent_guides(
15299        0..5,
15300        vec![
15301            indent_guide(buffer_id, 1, 3, 0),
15302            indent_guide(buffer_id, 2, 2, 1),
15303        ],
15304        None,
15305        &mut cx,
15306    );
15307}
15308
15309#[gpui::test]
15310async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15311    let (buffer_id, mut cx) = setup_indent_guides_editor(
15312        &"
15313        fn main() {
15314            let a = 1;
15315
15316            let c = 3;
15317        }"
15318        .unindent(),
15319        cx,
15320    )
15321    .await;
15322
15323    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15324}
15325
15326#[gpui::test]
15327async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15328    let (buffer_id, mut cx) = setup_indent_guides_editor(
15329        &"
15330        fn main() {
15331            let a = 1;
15332
15333            let c = 3;
15334
15335            if a == 3 {
15336                let b = 2;
15337            } else {
15338                let c = 3;
15339            }
15340        }"
15341        .unindent(),
15342        cx,
15343    )
15344    .await;
15345
15346    assert_indent_guides(
15347        0..11,
15348        vec![
15349            indent_guide(buffer_id, 1, 9, 0),
15350            indent_guide(buffer_id, 6, 6, 1),
15351            indent_guide(buffer_id, 8, 8, 1),
15352        ],
15353        None,
15354        &mut cx,
15355    );
15356}
15357
15358#[gpui::test]
15359async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15360    let (buffer_id, mut cx) = setup_indent_guides_editor(
15361        &"
15362        fn main() {
15363            let a = 1;
15364
15365            let c = 3;
15366
15367            if a == 3 {
15368                let b = 2;
15369            } else {
15370                let c = 3;
15371            }
15372        }"
15373        .unindent(),
15374        cx,
15375    )
15376    .await;
15377
15378    assert_indent_guides(
15379        1..11,
15380        vec![
15381            indent_guide(buffer_id, 1, 9, 0),
15382            indent_guide(buffer_id, 6, 6, 1),
15383            indent_guide(buffer_id, 8, 8, 1),
15384        ],
15385        None,
15386        &mut cx,
15387    );
15388}
15389
15390#[gpui::test]
15391async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15392    let (buffer_id, mut cx) = setup_indent_guides_editor(
15393        &"
15394        fn main() {
15395            let a = 1;
15396
15397            let c = 3;
15398
15399            if a == 3 {
15400                let b = 2;
15401            } else {
15402                let c = 3;
15403            }
15404        }"
15405        .unindent(),
15406        cx,
15407    )
15408    .await;
15409
15410    assert_indent_guides(
15411        1..10,
15412        vec![
15413            indent_guide(buffer_id, 1, 9, 0),
15414            indent_guide(buffer_id, 6, 6, 1),
15415            indent_guide(buffer_id, 8, 8, 1),
15416        ],
15417        None,
15418        &mut cx,
15419    );
15420}
15421
15422#[gpui::test]
15423async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15424    let (buffer_id, mut cx) = setup_indent_guides_editor(
15425        &"
15426        block1
15427            block2
15428                block3
15429                    block4
15430            block2
15431        block1
15432        block1"
15433            .unindent(),
15434        cx,
15435    )
15436    .await;
15437
15438    assert_indent_guides(
15439        1..10,
15440        vec![
15441            indent_guide(buffer_id, 1, 4, 0),
15442            indent_guide(buffer_id, 2, 3, 1),
15443            indent_guide(buffer_id, 3, 3, 2),
15444        ],
15445        None,
15446        &mut cx,
15447    );
15448}
15449
15450#[gpui::test]
15451async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15452    let (buffer_id, mut cx) = setup_indent_guides_editor(
15453        &"
15454        block1
15455            block2
15456                block3
15457
15458        block1
15459        block1"
15460            .unindent(),
15461        cx,
15462    )
15463    .await;
15464
15465    assert_indent_guides(
15466        0..6,
15467        vec![
15468            indent_guide(buffer_id, 1, 2, 0),
15469            indent_guide(buffer_id, 2, 2, 1),
15470        ],
15471        None,
15472        &mut cx,
15473    );
15474}
15475
15476#[gpui::test]
15477async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15478    let (buffer_id, mut cx) = setup_indent_guides_editor(
15479        &"
15480        block1
15481
15482
15483
15484            block2
15485        "
15486        .unindent(),
15487        cx,
15488    )
15489    .await;
15490
15491    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15492}
15493
15494#[gpui::test]
15495async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15496    let (buffer_id, mut cx) = setup_indent_guides_editor(
15497        &"
15498        def a:
15499        \tb = 3
15500        \tif True:
15501        \t\tc = 4
15502        \t\td = 5
15503        \tprint(b)
15504        "
15505        .unindent(),
15506        cx,
15507    )
15508    .await;
15509
15510    assert_indent_guides(
15511        0..6,
15512        vec![
15513            indent_guide(buffer_id, 1, 6, 0),
15514            indent_guide(buffer_id, 3, 4, 1),
15515        ],
15516        None,
15517        &mut cx,
15518    );
15519}
15520
15521#[gpui::test]
15522async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15523    let (buffer_id, mut cx) = setup_indent_guides_editor(
15524        &"
15525    fn main() {
15526        let a = 1;
15527    }"
15528        .unindent(),
15529        cx,
15530    )
15531    .await;
15532
15533    cx.update_editor(|editor, window, cx| {
15534        editor.change_selections(None, window, cx, |s| {
15535            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15536        });
15537    });
15538
15539    assert_indent_guides(
15540        0..3,
15541        vec![indent_guide(buffer_id, 1, 1, 0)],
15542        Some(vec![0]),
15543        &mut cx,
15544    );
15545}
15546
15547#[gpui::test]
15548async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15549    let (buffer_id, mut cx) = setup_indent_guides_editor(
15550        &"
15551    fn main() {
15552        if 1 == 2 {
15553            let a = 1;
15554        }
15555    }"
15556        .unindent(),
15557        cx,
15558    )
15559    .await;
15560
15561    cx.update_editor(|editor, window, cx| {
15562        editor.change_selections(None, window, cx, |s| {
15563            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15564        });
15565    });
15566
15567    assert_indent_guides(
15568        0..4,
15569        vec![
15570            indent_guide(buffer_id, 1, 3, 0),
15571            indent_guide(buffer_id, 2, 2, 1),
15572        ],
15573        Some(vec![1]),
15574        &mut cx,
15575    );
15576
15577    cx.update_editor(|editor, window, cx| {
15578        editor.change_selections(None, window, cx, |s| {
15579            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15580        });
15581    });
15582
15583    assert_indent_guides(
15584        0..4,
15585        vec![
15586            indent_guide(buffer_id, 1, 3, 0),
15587            indent_guide(buffer_id, 2, 2, 1),
15588        ],
15589        Some(vec![1]),
15590        &mut cx,
15591    );
15592
15593    cx.update_editor(|editor, window, cx| {
15594        editor.change_selections(None, window, cx, |s| {
15595            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15596        });
15597    });
15598
15599    assert_indent_guides(
15600        0..4,
15601        vec![
15602            indent_guide(buffer_id, 1, 3, 0),
15603            indent_guide(buffer_id, 2, 2, 1),
15604        ],
15605        Some(vec![0]),
15606        &mut cx,
15607    );
15608}
15609
15610#[gpui::test]
15611async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15612    let (buffer_id, mut cx) = setup_indent_guides_editor(
15613        &"
15614    fn main() {
15615        let a = 1;
15616
15617        let b = 2;
15618    }"
15619        .unindent(),
15620        cx,
15621    )
15622    .await;
15623
15624    cx.update_editor(|editor, window, cx| {
15625        editor.change_selections(None, window, cx, |s| {
15626            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15627        });
15628    });
15629
15630    assert_indent_guides(
15631        0..5,
15632        vec![indent_guide(buffer_id, 1, 3, 0)],
15633        Some(vec![0]),
15634        &mut cx,
15635    );
15636}
15637
15638#[gpui::test]
15639async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15640    let (buffer_id, mut cx) = setup_indent_guides_editor(
15641        &"
15642    def m:
15643        a = 1
15644        pass"
15645            .unindent(),
15646        cx,
15647    )
15648    .await;
15649
15650    cx.update_editor(|editor, window, cx| {
15651        editor.change_selections(None, window, cx, |s| {
15652            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15653        });
15654    });
15655
15656    assert_indent_guides(
15657        0..3,
15658        vec![indent_guide(buffer_id, 1, 2, 0)],
15659        Some(vec![0]),
15660        &mut cx,
15661    );
15662}
15663
15664#[gpui::test]
15665async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15666    init_test(cx, |_| {});
15667    let mut cx = EditorTestContext::new(cx).await;
15668    let text = indoc! {
15669        "
15670        impl A {
15671            fn b() {
15672                0;
15673                3;
15674                5;
15675                6;
15676                7;
15677            }
15678        }
15679        "
15680    };
15681    let base_text = indoc! {
15682        "
15683        impl A {
15684            fn b() {
15685                0;
15686                1;
15687                2;
15688                3;
15689                4;
15690            }
15691            fn c() {
15692                5;
15693                6;
15694                7;
15695            }
15696        }
15697        "
15698    };
15699
15700    cx.update_editor(|editor, window, cx| {
15701        editor.set_text(text, window, cx);
15702
15703        editor.buffer().update(cx, |multibuffer, cx| {
15704            let buffer = multibuffer.as_singleton().unwrap();
15705            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15706
15707            multibuffer.set_all_diff_hunks_expanded(cx);
15708            multibuffer.add_diff(diff, cx);
15709
15710            buffer.read(cx).remote_id()
15711        })
15712    });
15713    cx.run_until_parked();
15714
15715    cx.assert_state_with_diff(
15716        indoc! { "
15717          impl A {
15718              fn b() {
15719                  0;
15720        -         1;
15721        -         2;
15722                  3;
15723        -         4;
15724        -     }
15725        -     fn c() {
15726                  5;
15727                  6;
15728                  7;
15729              }
15730          }
15731          ˇ"
15732        }
15733        .to_string(),
15734    );
15735
15736    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15737        editor
15738            .snapshot(window, cx)
15739            .buffer_snapshot
15740            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15741            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15742            .collect::<Vec<_>>()
15743    });
15744    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15745    assert_eq!(
15746        actual_guides,
15747        vec![
15748            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15749            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15750            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15751        ]
15752    );
15753}
15754
15755#[gpui::test]
15756async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15757    init_test(cx, |_| {});
15758    let mut cx = EditorTestContext::new(cx).await;
15759
15760    let diff_base = r#"
15761        a
15762        b
15763        c
15764        "#
15765    .unindent();
15766
15767    cx.set_state(
15768        &r#"
15769        ˇA
15770        b
15771        C
15772        "#
15773        .unindent(),
15774    );
15775    cx.set_head_text(&diff_base);
15776    cx.update_editor(|editor, window, cx| {
15777        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15778    });
15779    executor.run_until_parked();
15780
15781    let both_hunks_expanded = r#"
15782        - a
15783        + ˇA
15784          b
15785        - c
15786        + C
15787        "#
15788    .unindent();
15789
15790    cx.assert_state_with_diff(both_hunks_expanded.clone());
15791
15792    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15793        let snapshot = editor.snapshot(window, cx);
15794        let hunks = editor
15795            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15796            .collect::<Vec<_>>();
15797        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15798        let buffer_id = hunks[0].buffer_id;
15799        hunks
15800            .into_iter()
15801            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15802            .collect::<Vec<_>>()
15803    });
15804    assert_eq!(hunk_ranges.len(), 2);
15805
15806    cx.update_editor(|editor, _, cx| {
15807        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15808    });
15809    executor.run_until_parked();
15810
15811    let second_hunk_expanded = r#"
15812          ˇA
15813          b
15814        - c
15815        + C
15816        "#
15817    .unindent();
15818
15819    cx.assert_state_with_diff(second_hunk_expanded);
15820
15821    cx.update_editor(|editor, _, cx| {
15822        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15823    });
15824    executor.run_until_parked();
15825
15826    cx.assert_state_with_diff(both_hunks_expanded.clone());
15827
15828    cx.update_editor(|editor, _, cx| {
15829        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15830    });
15831    executor.run_until_parked();
15832
15833    let first_hunk_expanded = r#"
15834        - a
15835        + ˇA
15836          b
15837          C
15838        "#
15839    .unindent();
15840
15841    cx.assert_state_with_diff(first_hunk_expanded);
15842
15843    cx.update_editor(|editor, _, cx| {
15844        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15845    });
15846    executor.run_until_parked();
15847
15848    cx.assert_state_with_diff(both_hunks_expanded);
15849
15850    cx.set_state(
15851        &r#"
15852        ˇA
15853        b
15854        "#
15855        .unindent(),
15856    );
15857    cx.run_until_parked();
15858
15859    // TODO this cursor position seems bad
15860    cx.assert_state_with_diff(
15861        r#"
15862        - ˇa
15863        + A
15864          b
15865        "#
15866        .unindent(),
15867    );
15868
15869    cx.update_editor(|editor, window, cx| {
15870        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15871    });
15872
15873    cx.assert_state_with_diff(
15874        r#"
15875            - ˇa
15876            + A
15877              b
15878            - c
15879            "#
15880        .unindent(),
15881    );
15882
15883    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15884        let snapshot = editor.snapshot(window, cx);
15885        let hunks = editor
15886            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15887            .collect::<Vec<_>>();
15888        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15889        let buffer_id = hunks[0].buffer_id;
15890        hunks
15891            .into_iter()
15892            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15893            .collect::<Vec<_>>()
15894    });
15895    assert_eq!(hunk_ranges.len(), 2);
15896
15897    cx.update_editor(|editor, _, cx| {
15898        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15899    });
15900    executor.run_until_parked();
15901
15902    cx.assert_state_with_diff(
15903        r#"
15904        - ˇa
15905        + A
15906          b
15907        "#
15908        .unindent(),
15909    );
15910}
15911
15912#[gpui::test]
15913async fn test_toggle_deletion_hunk_at_start_of_file(
15914    executor: BackgroundExecutor,
15915    cx: &mut TestAppContext,
15916) {
15917    init_test(cx, |_| {});
15918    let mut cx = EditorTestContext::new(cx).await;
15919
15920    let diff_base = r#"
15921        a
15922        b
15923        c
15924        "#
15925    .unindent();
15926
15927    cx.set_state(
15928        &r#"
15929        ˇb
15930        c
15931        "#
15932        .unindent(),
15933    );
15934    cx.set_head_text(&diff_base);
15935    cx.update_editor(|editor, window, cx| {
15936        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15937    });
15938    executor.run_until_parked();
15939
15940    let hunk_expanded = r#"
15941        - a
15942          ˇb
15943          c
15944        "#
15945    .unindent();
15946
15947    cx.assert_state_with_diff(hunk_expanded.clone());
15948
15949    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15950        let snapshot = editor.snapshot(window, cx);
15951        let hunks = editor
15952            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15953            .collect::<Vec<_>>();
15954        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15955        let buffer_id = hunks[0].buffer_id;
15956        hunks
15957            .into_iter()
15958            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15959            .collect::<Vec<_>>()
15960    });
15961    assert_eq!(hunk_ranges.len(), 1);
15962
15963    cx.update_editor(|editor, _, cx| {
15964        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15965    });
15966    executor.run_until_parked();
15967
15968    let hunk_collapsed = r#"
15969          ˇb
15970          c
15971        "#
15972    .unindent();
15973
15974    cx.assert_state_with_diff(hunk_collapsed);
15975
15976    cx.update_editor(|editor, _, cx| {
15977        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15978    });
15979    executor.run_until_parked();
15980
15981    cx.assert_state_with_diff(hunk_expanded.clone());
15982}
15983
15984#[gpui::test]
15985async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15986    init_test(cx, |_| {});
15987
15988    let fs = FakeFs::new(cx.executor());
15989    fs.insert_tree(
15990        path!("/test"),
15991        json!({
15992            ".git": {},
15993            "file-1": "ONE\n",
15994            "file-2": "TWO\n",
15995            "file-3": "THREE\n",
15996        }),
15997    )
15998    .await;
15999
16000    fs.set_head_for_repo(
16001        path!("/test/.git").as_ref(),
16002        &[
16003            ("file-1".into(), "one\n".into()),
16004            ("file-2".into(), "two\n".into()),
16005            ("file-3".into(), "three\n".into()),
16006        ],
16007    );
16008
16009    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16010    let mut buffers = vec![];
16011    for i in 1..=3 {
16012        let buffer = project
16013            .update(cx, |project, cx| {
16014                let path = format!(path!("/test/file-{}"), i);
16015                project.open_local_buffer(path, cx)
16016            })
16017            .await
16018            .unwrap();
16019        buffers.push(buffer);
16020    }
16021
16022    let multibuffer = cx.new(|cx| {
16023        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16024        multibuffer.set_all_diff_hunks_expanded(cx);
16025        for buffer in &buffers {
16026            let snapshot = buffer.read(cx).snapshot();
16027            multibuffer.set_excerpts_for_path(
16028                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16029                buffer.clone(),
16030                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16031                DEFAULT_MULTIBUFFER_CONTEXT,
16032                cx,
16033            );
16034        }
16035        multibuffer
16036    });
16037
16038    let editor = cx.add_window(|window, cx| {
16039        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16040    });
16041    cx.run_until_parked();
16042
16043    let snapshot = editor
16044        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16045        .unwrap();
16046    let hunks = snapshot
16047        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16048        .map(|hunk| match hunk {
16049            DisplayDiffHunk::Unfolded {
16050                display_row_range, ..
16051            } => display_row_range,
16052            DisplayDiffHunk::Folded { .. } => unreachable!(),
16053        })
16054        .collect::<Vec<_>>();
16055    assert_eq!(
16056        hunks,
16057        [
16058            DisplayRow(2)..DisplayRow(4),
16059            DisplayRow(7)..DisplayRow(9),
16060            DisplayRow(12)..DisplayRow(14),
16061        ]
16062    );
16063}
16064
16065#[gpui::test]
16066async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16067    init_test(cx, |_| {});
16068
16069    let mut cx = EditorTestContext::new(cx).await;
16070    cx.set_head_text(indoc! { "
16071        one
16072        two
16073        three
16074        four
16075        five
16076        "
16077    });
16078    cx.set_index_text(indoc! { "
16079        one
16080        two
16081        three
16082        four
16083        five
16084        "
16085    });
16086    cx.set_state(indoc! {"
16087        one
16088        TWO
16089        ˇTHREE
16090        FOUR
16091        five
16092    "});
16093    cx.run_until_parked();
16094    cx.update_editor(|editor, window, cx| {
16095        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16096    });
16097    cx.run_until_parked();
16098    cx.assert_index_text(Some(indoc! {"
16099        one
16100        TWO
16101        THREE
16102        FOUR
16103        five
16104    "}));
16105    cx.set_state(indoc! { "
16106        one
16107        TWO
16108        ˇTHREE-HUNDRED
16109        FOUR
16110        five
16111    "});
16112    cx.run_until_parked();
16113    cx.update_editor(|editor, window, cx| {
16114        let snapshot = editor.snapshot(window, cx);
16115        let hunks = editor
16116            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16117            .collect::<Vec<_>>();
16118        assert_eq!(hunks.len(), 1);
16119        assert_eq!(
16120            hunks[0].status(),
16121            DiffHunkStatus {
16122                kind: DiffHunkStatusKind::Modified,
16123                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16124            }
16125        );
16126
16127        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16128    });
16129    cx.run_until_parked();
16130    cx.assert_index_text(Some(indoc! {"
16131        one
16132        TWO
16133        THREE-HUNDRED
16134        FOUR
16135        five
16136    "}));
16137}
16138
16139#[gpui::test]
16140fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16141    init_test(cx, |_| {});
16142
16143    let editor = cx.add_window(|window, cx| {
16144        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16145        build_editor(buffer, window, cx)
16146    });
16147
16148    let render_args = Arc::new(Mutex::new(None));
16149    let snapshot = editor
16150        .update(cx, |editor, window, cx| {
16151            let snapshot = editor.buffer().read(cx).snapshot(cx);
16152            let range =
16153                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16154
16155            struct RenderArgs {
16156                row: MultiBufferRow,
16157                folded: bool,
16158                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16159            }
16160
16161            let crease = Crease::inline(
16162                range,
16163                FoldPlaceholder::test(),
16164                {
16165                    let toggle_callback = render_args.clone();
16166                    move |row, folded, callback, _window, _cx| {
16167                        *toggle_callback.lock() = Some(RenderArgs {
16168                            row,
16169                            folded,
16170                            callback,
16171                        });
16172                        div()
16173                    }
16174                },
16175                |_row, _folded, _window, _cx| div(),
16176            );
16177
16178            editor.insert_creases(Some(crease), cx);
16179            let snapshot = editor.snapshot(window, cx);
16180            let _div = snapshot.render_crease_toggle(
16181                MultiBufferRow(1),
16182                false,
16183                cx.entity().clone(),
16184                window,
16185                cx,
16186            );
16187            snapshot
16188        })
16189        .unwrap();
16190
16191    let render_args = render_args.lock().take().unwrap();
16192    assert_eq!(render_args.row, MultiBufferRow(1));
16193    assert!(!render_args.folded);
16194    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16195
16196    cx.update_window(*editor, |_, window, cx| {
16197        (render_args.callback)(true, window, cx)
16198    })
16199    .unwrap();
16200    let snapshot = editor
16201        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16202        .unwrap();
16203    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16204
16205    cx.update_window(*editor, |_, window, cx| {
16206        (render_args.callback)(false, window, cx)
16207    })
16208    .unwrap();
16209    let snapshot = editor
16210        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16211        .unwrap();
16212    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16213}
16214
16215#[gpui::test]
16216async fn test_input_text(cx: &mut TestAppContext) {
16217    init_test(cx, |_| {});
16218    let mut cx = EditorTestContext::new(cx).await;
16219
16220    cx.set_state(
16221        &r#"ˇone
16222        two
16223
16224        three
16225        fourˇ
16226        five
16227
16228        siˇx"#
16229            .unindent(),
16230    );
16231
16232    cx.dispatch_action(HandleInput(String::new()));
16233    cx.assert_editor_state(
16234        &r#"ˇone
16235        two
16236
16237        three
16238        fourˇ
16239        five
16240
16241        siˇx"#
16242            .unindent(),
16243    );
16244
16245    cx.dispatch_action(HandleInput("AAAA".to_string()));
16246    cx.assert_editor_state(
16247        &r#"AAAAˇone
16248        two
16249
16250        three
16251        fourAAAAˇ
16252        five
16253
16254        siAAAAˇx"#
16255            .unindent(),
16256    );
16257}
16258
16259#[gpui::test]
16260async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16261    init_test(cx, |_| {});
16262
16263    let mut cx = EditorTestContext::new(cx).await;
16264    cx.set_state(
16265        r#"let foo = 1;
16266let foo = 2;
16267let foo = 3;
16268let fooˇ = 4;
16269let foo = 5;
16270let foo = 6;
16271let foo = 7;
16272let foo = 8;
16273let foo = 9;
16274let foo = 10;
16275let foo = 11;
16276let foo = 12;
16277let foo = 13;
16278let foo = 14;
16279let foo = 15;"#,
16280    );
16281
16282    cx.update_editor(|e, window, cx| {
16283        assert_eq!(
16284            e.next_scroll_position,
16285            NextScrollCursorCenterTopBottom::Center,
16286            "Default next scroll direction is center",
16287        );
16288
16289        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16290        assert_eq!(
16291            e.next_scroll_position,
16292            NextScrollCursorCenterTopBottom::Top,
16293            "After center, next scroll direction should be top",
16294        );
16295
16296        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16297        assert_eq!(
16298            e.next_scroll_position,
16299            NextScrollCursorCenterTopBottom::Bottom,
16300            "After top, next scroll direction should be bottom",
16301        );
16302
16303        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16304        assert_eq!(
16305            e.next_scroll_position,
16306            NextScrollCursorCenterTopBottom::Center,
16307            "After bottom, scrolling should start over",
16308        );
16309
16310        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16311        assert_eq!(
16312            e.next_scroll_position,
16313            NextScrollCursorCenterTopBottom::Top,
16314            "Scrolling continues if retriggered fast enough"
16315        );
16316    });
16317
16318    cx.executor()
16319        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16320    cx.executor().run_until_parked();
16321    cx.update_editor(|e, _, _| {
16322        assert_eq!(
16323            e.next_scroll_position,
16324            NextScrollCursorCenterTopBottom::Center,
16325            "If scrolling is not triggered fast enough, it should reset"
16326        );
16327    });
16328}
16329
16330#[gpui::test]
16331async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16332    init_test(cx, |_| {});
16333    let mut cx = EditorLspTestContext::new_rust(
16334        lsp::ServerCapabilities {
16335            definition_provider: Some(lsp::OneOf::Left(true)),
16336            references_provider: Some(lsp::OneOf::Left(true)),
16337            ..lsp::ServerCapabilities::default()
16338        },
16339        cx,
16340    )
16341    .await;
16342
16343    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16344        let go_to_definition = cx
16345            .lsp
16346            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16347                move |params, _| async move {
16348                    if empty_go_to_definition {
16349                        Ok(None)
16350                    } else {
16351                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16352                            uri: params.text_document_position_params.text_document.uri,
16353                            range: lsp::Range::new(
16354                                lsp::Position::new(4, 3),
16355                                lsp::Position::new(4, 6),
16356                            ),
16357                        })))
16358                    }
16359                },
16360            );
16361        let references = cx
16362            .lsp
16363            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16364                Ok(Some(vec![lsp::Location {
16365                    uri: params.text_document_position.text_document.uri,
16366                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16367                }]))
16368            });
16369        (go_to_definition, references)
16370    };
16371
16372    cx.set_state(
16373        &r#"fn one() {
16374            let mut a = ˇtwo();
16375        }
16376
16377        fn two() {}"#
16378            .unindent(),
16379    );
16380    set_up_lsp_handlers(false, &mut cx);
16381    let navigated = cx
16382        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16383        .await
16384        .expect("Failed to navigate to definition");
16385    assert_eq!(
16386        navigated,
16387        Navigated::Yes,
16388        "Should have navigated to definition from the GetDefinition response"
16389    );
16390    cx.assert_editor_state(
16391        &r#"fn one() {
16392            let mut a = two();
16393        }
16394
16395        fn «twoˇ»() {}"#
16396            .unindent(),
16397    );
16398
16399    let editors = cx.update_workspace(|workspace, _, cx| {
16400        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16401    });
16402    cx.update_editor(|_, _, test_editor_cx| {
16403        assert_eq!(
16404            editors.len(),
16405            1,
16406            "Initially, only one, test, editor should be open in the workspace"
16407        );
16408        assert_eq!(
16409            test_editor_cx.entity(),
16410            editors.last().expect("Asserted len is 1").clone()
16411        );
16412    });
16413
16414    set_up_lsp_handlers(true, &mut cx);
16415    let navigated = cx
16416        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16417        .await
16418        .expect("Failed to navigate to lookup references");
16419    assert_eq!(
16420        navigated,
16421        Navigated::Yes,
16422        "Should have navigated to references as a fallback after empty GoToDefinition response"
16423    );
16424    // We should not change the selections in the existing file,
16425    // if opening another milti buffer with the references
16426    cx.assert_editor_state(
16427        &r#"fn one() {
16428            let mut a = two();
16429        }
16430
16431        fn «twoˇ»() {}"#
16432            .unindent(),
16433    );
16434    let editors = cx.update_workspace(|workspace, _, cx| {
16435        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16436    });
16437    cx.update_editor(|_, _, test_editor_cx| {
16438        assert_eq!(
16439            editors.len(),
16440            2,
16441            "After falling back to references search, we open a new editor with the results"
16442        );
16443        let references_fallback_text = editors
16444            .into_iter()
16445            .find(|new_editor| *new_editor != test_editor_cx.entity())
16446            .expect("Should have one non-test editor now")
16447            .read(test_editor_cx)
16448            .text(test_editor_cx);
16449        assert_eq!(
16450            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16451            "Should use the range from the references response and not the GoToDefinition one"
16452        );
16453    });
16454}
16455
16456#[gpui::test]
16457async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16458    init_test(cx, |_| {});
16459    cx.update(|cx| {
16460        let mut editor_settings = EditorSettings::get_global(cx).clone();
16461        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16462        EditorSettings::override_global(editor_settings, cx);
16463    });
16464    let mut cx = EditorLspTestContext::new_rust(
16465        lsp::ServerCapabilities {
16466            definition_provider: Some(lsp::OneOf::Left(true)),
16467            references_provider: Some(lsp::OneOf::Left(true)),
16468            ..lsp::ServerCapabilities::default()
16469        },
16470        cx,
16471    )
16472    .await;
16473    let original_state = r#"fn one() {
16474        let mut a = ˇtwo();
16475    }
16476
16477    fn two() {}"#
16478        .unindent();
16479    cx.set_state(&original_state);
16480
16481    let mut go_to_definition = cx
16482        .lsp
16483        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16484            move |_, _| async move { Ok(None) },
16485        );
16486    let _references = cx
16487        .lsp
16488        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16489            panic!("Should not call for references with no go to definition fallback")
16490        });
16491
16492    let navigated = cx
16493        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16494        .await
16495        .expect("Failed to navigate to lookup references");
16496    go_to_definition
16497        .next()
16498        .await
16499        .expect("Should have called the go_to_definition handler");
16500
16501    assert_eq!(
16502        navigated,
16503        Navigated::No,
16504        "Should have navigated to references as a fallback after empty GoToDefinition response"
16505    );
16506    cx.assert_editor_state(&original_state);
16507    let editors = cx.update_workspace(|workspace, _, cx| {
16508        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16509    });
16510    cx.update_editor(|_, _, _| {
16511        assert_eq!(
16512            editors.len(),
16513            1,
16514            "After unsuccessful fallback, no other editor should have been opened"
16515        );
16516    });
16517}
16518
16519#[gpui::test]
16520async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16521    init_test(cx, |_| {});
16522
16523    let language = Arc::new(Language::new(
16524        LanguageConfig::default(),
16525        Some(tree_sitter_rust::LANGUAGE.into()),
16526    ));
16527
16528    let text = r#"
16529        #[cfg(test)]
16530        mod tests() {
16531            #[test]
16532            fn runnable_1() {
16533                let a = 1;
16534            }
16535
16536            #[test]
16537            fn runnable_2() {
16538                let a = 1;
16539                let b = 2;
16540            }
16541        }
16542    "#
16543    .unindent();
16544
16545    let fs = FakeFs::new(cx.executor());
16546    fs.insert_file("/file.rs", Default::default()).await;
16547
16548    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16549    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16550    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16551    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16552    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16553
16554    let editor = cx.new_window_entity(|window, cx| {
16555        Editor::new(
16556            EditorMode::Full,
16557            multi_buffer,
16558            Some(project.clone()),
16559            window,
16560            cx,
16561        )
16562    });
16563
16564    editor.update_in(cx, |editor, window, cx| {
16565        let snapshot = editor.buffer().read(cx).snapshot(cx);
16566        editor.tasks.insert(
16567            (buffer.read(cx).remote_id(), 3),
16568            RunnableTasks {
16569                templates: vec![],
16570                offset: snapshot.anchor_before(43),
16571                column: 0,
16572                extra_variables: HashMap::default(),
16573                context_range: BufferOffset(43)..BufferOffset(85),
16574            },
16575        );
16576        editor.tasks.insert(
16577            (buffer.read(cx).remote_id(), 8),
16578            RunnableTasks {
16579                templates: vec![],
16580                offset: snapshot.anchor_before(86),
16581                column: 0,
16582                extra_variables: HashMap::default(),
16583                context_range: BufferOffset(86)..BufferOffset(191),
16584            },
16585        );
16586
16587        // Test finding task when cursor is inside function body
16588        editor.change_selections(None, window, cx, |s| {
16589            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16590        });
16591        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16592        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16593
16594        // Test finding task when cursor is on function name
16595        editor.change_selections(None, window, cx, |s| {
16596            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16597        });
16598        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16599        assert_eq!(row, 8, "Should find task when cursor is on function name");
16600    });
16601}
16602
16603#[gpui::test]
16604async fn test_folding_buffers(cx: &mut TestAppContext) {
16605    init_test(cx, |_| {});
16606
16607    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16608    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16609    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16610
16611    let fs = FakeFs::new(cx.executor());
16612    fs.insert_tree(
16613        path!("/a"),
16614        json!({
16615            "first.rs": sample_text_1,
16616            "second.rs": sample_text_2,
16617            "third.rs": sample_text_3,
16618        }),
16619    )
16620    .await;
16621    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16622    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16623    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16624    let worktree = project.update(cx, |project, cx| {
16625        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16626        assert_eq!(worktrees.len(), 1);
16627        worktrees.pop().unwrap()
16628    });
16629    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16630
16631    let buffer_1 = project
16632        .update(cx, |project, cx| {
16633            project.open_buffer((worktree_id, "first.rs"), cx)
16634        })
16635        .await
16636        .unwrap();
16637    let buffer_2 = project
16638        .update(cx, |project, cx| {
16639            project.open_buffer((worktree_id, "second.rs"), cx)
16640        })
16641        .await
16642        .unwrap();
16643    let buffer_3 = project
16644        .update(cx, |project, cx| {
16645            project.open_buffer((worktree_id, "third.rs"), cx)
16646        })
16647        .await
16648        .unwrap();
16649
16650    let multi_buffer = cx.new(|cx| {
16651        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16652        multi_buffer.push_excerpts(
16653            buffer_1.clone(),
16654            [
16655                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16656                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16657                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16658            ],
16659            cx,
16660        );
16661        multi_buffer.push_excerpts(
16662            buffer_2.clone(),
16663            [
16664                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16665                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16666                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16667            ],
16668            cx,
16669        );
16670        multi_buffer.push_excerpts(
16671            buffer_3.clone(),
16672            [
16673                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16674                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16675                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16676            ],
16677            cx,
16678        );
16679        multi_buffer
16680    });
16681    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16682        Editor::new(
16683            EditorMode::Full,
16684            multi_buffer.clone(),
16685            Some(project.clone()),
16686            window,
16687            cx,
16688        )
16689    });
16690
16691    assert_eq!(
16692        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16693        "\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",
16694    );
16695
16696    multi_buffer_editor.update(cx, |editor, cx| {
16697        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16698    });
16699    assert_eq!(
16700        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16701        "\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",
16702        "After folding the first buffer, its text should not be displayed"
16703    );
16704
16705    multi_buffer_editor.update(cx, |editor, cx| {
16706        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16707    });
16708    assert_eq!(
16709        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16710        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16711        "After folding the second buffer, its text should not be displayed"
16712    );
16713
16714    multi_buffer_editor.update(cx, |editor, cx| {
16715        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16716    });
16717    assert_eq!(
16718        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16719        "\n\n\n\n\n",
16720        "After folding the third buffer, its text should not be displayed"
16721    );
16722
16723    // Emulate selection inside the fold logic, that should work
16724    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16725        editor
16726            .snapshot(window, cx)
16727            .next_line_boundary(Point::new(0, 4));
16728    });
16729
16730    multi_buffer_editor.update(cx, |editor, cx| {
16731        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16732    });
16733    assert_eq!(
16734        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16735        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16736        "After unfolding the second buffer, its text should be displayed"
16737    );
16738
16739    // Typing inside of buffer 1 causes that buffer to be unfolded.
16740    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16741        assert_eq!(
16742            multi_buffer
16743                .read(cx)
16744                .snapshot(cx)
16745                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16746                .collect::<String>(),
16747            "bbbb"
16748        );
16749        editor.change_selections(None, window, cx, |selections| {
16750            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16751        });
16752        editor.handle_input("B", window, cx);
16753    });
16754
16755    assert_eq!(
16756        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16757        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16758        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16759    );
16760
16761    multi_buffer_editor.update(cx, |editor, cx| {
16762        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16763    });
16764    assert_eq!(
16765        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16766        "\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",
16767        "After unfolding the all buffers, all original text should be displayed"
16768    );
16769}
16770
16771#[gpui::test]
16772async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16773    init_test(cx, |_| {});
16774
16775    let sample_text_1 = "1111\n2222\n3333".to_string();
16776    let sample_text_2 = "4444\n5555\n6666".to_string();
16777    let sample_text_3 = "7777\n8888\n9999".to_string();
16778
16779    let fs = FakeFs::new(cx.executor());
16780    fs.insert_tree(
16781        path!("/a"),
16782        json!({
16783            "first.rs": sample_text_1,
16784            "second.rs": sample_text_2,
16785            "third.rs": sample_text_3,
16786        }),
16787    )
16788    .await;
16789    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16790    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16791    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16792    let worktree = project.update(cx, |project, cx| {
16793        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16794        assert_eq!(worktrees.len(), 1);
16795        worktrees.pop().unwrap()
16796    });
16797    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16798
16799    let buffer_1 = project
16800        .update(cx, |project, cx| {
16801            project.open_buffer((worktree_id, "first.rs"), cx)
16802        })
16803        .await
16804        .unwrap();
16805    let buffer_2 = project
16806        .update(cx, |project, cx| {
16807            project.open_buffer((worktree_id, "second.rs"), cx)
16808        })
16809        .await
16810        .unwrap();
16811    let buffer_3 = project
16812        .update(cx, |project, cx| {
16813            project.open_buffer((worktree_id, "third.rs"), cx)
16814        })
16815        .await
16816        .unwrap();
16817
16818    let multi_buffer = cx.new(|cx| {
16819        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16820        multi_buffer.push_excerpts(
16821            buffer_1.clone(),
16822            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16823            cx,
16824        );
16825        multi_buffer.push_excerpts(
16826            buffer_2.clone(),
16827            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16828            cx,
16829        );
16830        multi_buffer.push_excerpts(
16831            buffer_3.clone(),
16832            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16833            cx,
16834        );
16835        multi_buffer
16836    });
16837
16838    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16839        Editor::new(
16840            EditorMode::Full,
16841            multi_buffer,
16842            Some(project.clone()),
16843            window,
16844            cx,
16845        )
16846    });
16847
16848    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16849    assert_eq!(
16850        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16851        full_text,
16852    );
16853
16854    multi_buffer_editor.update(cx, |editor, cx| {
16855        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16856    });
16857    assert_eq!(
16858        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16859        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16860        "After folding the first buffer, its text should not be displayed"
16861    );
16862
16863    multi_buffer_editor.update(cx, |editor, cx| {
16864        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16865    });
16866
16867    assert_eq!(
16868        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16869        "\n\n\n\n\n\n7777\n8888\n9999",
16870        "After folding the second buffer, its text should not be displayed"
16871    );
16872
16873    multi_buffer_editor.update(cx, |editor, cx| {
16874        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16875    });
16876    assert_eq!(
16877        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16878        "\n\n\n\n\n",
16879        "After folding the third buffer, its text should not be displayed"
16880    );
16881
16882    multi_buffer_editor.update(cx, |editor, cx| {
16883        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16884    });
16885    assert_eq!(
16886        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16887        "\n\n\n\n4444\n5555\n6666\n\n",
16888        "After unfolding the second buffer, its text should be displayed"
16889    );
16890
16891    multi_buffer_editor.update(cx, |editor, cx| {
16892        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16893    });
16894    assert_eq!(
16895        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16896        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16897        "After unfolding the first buffer, its text should be displayed"
16898    );
16899
16900    multi_buffer_editor.update(cx, |editor, cx| {
16901        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16902    });
16903    assert_eq!(
16904        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16905        full_text,
16906        "After unfolding all buffers, all original text should be displayed"
16907    );
16908}
16909
16910#[gpui::test]
16911async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16912    init_test(cx, |_| {});
16913
16914    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16915
16916    let fs = FakeFs::new(cx.executor());
16917    fs.insert_tree(
16918        path!("/a"),
16919        json!({
16920            "main.rs": sample_text,
16921        }),
16922    )
16923    .await;
16924    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16925    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16926    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16927    let worktree = project.update(cx, |project, cx| {
16928        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16929        assert_eq!(worktrees.len(), 1);
16930        worktrees.pop().unwrap()
16931    });
16932    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16933
16934    let buffer_1 = project
16935        .update(cx, |project, cx| {
16936            project.open_buffer((worktree_id, "main.rs"), cx)
16937        })
16938        .await
16939        .unwrap();
16940
16941    let multi_buffer = cx.new(|cx| {
16942        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16943        multi_buffer.push_excerpts(
16944            buffer_1.clone(),
16945            [ExcerptRange::new(
16946                Point::new(0, 0)
16947                    ..Point::new(
16948                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16949                        0,
16950                    ),
16951            )],
16952            cx,
16953        );
16954        multi_buffer
16955    });
16956    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16957        Editor::new(
16958            EditorMode::Full,
16959            multi_buffer,
16960            Some(project.clone()),
16961            window,
16962            cx,
16963        )
16964    });
16965
16966    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16967    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16968        enum TestHighlight {}
16969        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16970        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16971        editor.highlight_text::<TestHighlight>(
16972            vec![highlight_range.clone()],
16973            HighlightStyle::color(Hsla::green()),
16974            cx,
16975        );
16976        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16977    });
16978
16979    let full_text = format!("\n\n{sample_text}");
16980    assert_eq!(
16981        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16982        full_text,
16983    );
16984}
16985
16986#[gpui::test]
16987async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16988    init_test(cx, |_| {});
16989    cx.update(|cx| {
16990        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16991            "keymaps/default-linux.json",
16992            cx,
16993        )
16994        .unwrap();
16995        cx.bind_keys(default_key_bindings);
16996    });
16997
16998    let (editor, cx) = cx.add_window_view(|window, cx| {
16999        let multi_buffer = MultiBuffer::build_multi(
17000            [
17001                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17002                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17003                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17004                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17005            ],
17006            cx,
17007        );
17008        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17009
17010        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17011        // fold all but the second buffer, so that we test navigating between two
17012        // adjacent folded buffers, as well as folded buffers at the start and
17013        // end the multibuffer
17014        editor.fold_buffer(buffer_ids[0], cx);
17015        editor.fold_buffer(buffer_ids[2], cx);
17016        editor.fold_buffer(buffer_ids[3], cx);
17017
17018        editor
17019    });
17020    cx.simulate_resize(size(px(1000.), px(1000.)));
17021
17022    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17023    cx.assert_excerpts_with_selections(indoc! {"
17024        [EXCERPT]
17025        ˇ[FOLDED]
17026        [EXCERPT]
17027        a1
17028        b1
17029        [EXCERPT]
17030        [FOLDED]
17031        [EXCERPT]
17032        [FOLDED]
17033        "
17034    });
17035    cx.simulate_keystroke("down");
17036    cx.assert_excerpts_with_selections(indoc! {"
17037        [EXCERPT]
17038        [FOLDED]
17039        [EXCERPT]
17040        ˇa1
17041        b1
17042        [EXCERPT]
17043        [FOLDED]
17044        [EXCERPT]
17045        [FOLDED]
17046        "
17047    });
17048    cx.simulate_keystroke("down");
17049    cx.assert_excerpts_with_selections(indoc! {"
17050        [EXCERPT]
17051        [FOLDED]
17052        [EXCERPT]
17053        a1
17054        ˇb1
17055        [EXCERPT]
17056        [FOLDED]
17057        [EXCERPT]
17058        [FOLDED]
17059        "
17060    });
17061    cx.simulate_keystroke("down");
17062    cx.assert_excerpts_with_selections(indoc! {"
17063        [EXCERPT]
17064        [FOLDED]
17065        [EXCERPT]
17066        a1
17067        b1
17068        ˇ[EXCERPT]
17069        [FOLDED]
17070        [EXCERPT]
17071        [FOLDED]
17072        "
17073    });
17074    cx.simulate_keystroke("down");
17075    cx.assert_excerpts_with_selections(indoc! {"
17076        [EXCERPT]
17077        [FOLDED]
17078        [EXCERPT]
17079        a1
17080        b1
17081        [EXCERPT]
17082        ˇ[FOLDED]
17083        [EXCERPT]
17084        [FOLDED]
17085        "
17086    });
17087    for _ in 0..5 {
17088        cx.simulate_keystroke("down");
17089        cx.assert_excerpts_with_selections(indoc! {"
17090            [EXCERPT]
17091            [FOLDED]
17092            [EXCERPT]
17093            a1
17094            b1
17095            [EXCERPT]
17096            [FOLDED]
17097            [EXCERPT]
17098            ˇ[FOLDED]
17099            "
17100        });
17101    }
17102
17103    cx.simulate_keystroke("up");
17104    cx.assert_excerpts_with_selections(indoc! {"
17105        [EXCERPT]
17106        [FOLDED]
17107        [EXCERPT]
17108        a1
17109        b1
17110        [EXCERPT]
17111        ˇ[FOLDED]
17112        [EXCERPT]
17113        [FOLDED]
17114        "
17115    });
17116    cx.simulate_keystroke("up");
17117    cx.assert_excerpts_with_selections(indoc! {"
17118        [EXCERPT]
17119        [FOLDED]
17120        [EXCERPT]
17121        a1
17122        b1
17123        ˇ[EXCERPT]
17124        [FOLDED]
17125        [EXCERPT]
17126        [FOLDED]
17127        "
17128    });
17129    cx.simulate_keystroke("up");
17130    cx.assert_excerpts_with_selections(indoc! {"
17131        [EXCERPT]
17132        [FOLDED]
17133        [EXCERPT]
17134        a1
17135        ˇb1
17136        [EXCERPT]
17137        [FOLDED]
17138        [EXCERPT]
17139        [FOLDED]
17140        "
17141    });
17142    cx.simulate_keystroke("up");
17143    cx.assert_excerpts_with_selections(indoc! {"
17144        [EXCERPT]
17145        [FOLDED]
17146        [EXCERPT]
17147        ˇa1
17148        b1
17149        [EXCERPT]
17150        [FOLDED]
17151        [EXCERPT]
17152        [FOLDED]
17153        "
17154    });
17155    for _ in 0..5 {
17156        cx.simulate_keystroke("up");
17157        cx.assert_excerpts_with_selections(indoc! {"
17158            [EXCERPT]
17159            ˇ[FOLDED]
17160            [EXCERPT]
17161            a1
17162            b1
17163            [EXCERPT]
17164            [FOLDED]
17165            [EXCERPT]
17166            [FOLDED]
17167            "
17168        });
17169    }
17170}
17171
17172#[gpui::test]
17173async fn test_inline_completion_text(cx: &mut TestAppContext) {
17174    init_test(cx, |_| {});
17175
17176    // Simple insertion
17177    assert_highlighted_edits(
17178        "Hello, world!",
17179        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17180        true,
17181        cx,
17182        |highlighted_edits, cx| {
17183            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17184            assert_eq!(highlighted_edits.highlights.len(), 1);
17185            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17186            assert_eq!(
17187                highlighted_edits.highlights[0].1.background_color,
17188                Some(cx.theme().status().created_background)
17189            );
17190        },
17191    )
17192    .await;
17193
17194    // Replacement
17195    assert_highlighted_edits(
17196        "This is a test.",
17197        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17198        false,
17199        cx,
17200        |highlighted_edits, cx| {
17201            assert_eq!(highlighted_edits.text, "That is a test.");
17202            assert_eq!(highlighted_edits.highlights.len(), 1);
17203            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17204            assert_eq!(
17205                highlighted_edits.highlights[0].1.background_color,
17206                Some(cx.theme().status().created_background)
17207            );
17208        },
17209    )
17210    .await;
17211
17212    // Multiple edits
17213    assert_highlighted_edits(
17214        "Hello, world!",
17215        vec![
17216            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17217            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17218        ],
17219        false,
17220        cx,
17221        |highlighted_edits, cx| {
17222            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17223            assert_eq!(highlighted_edits.highlights.len(), 2);
17224            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17225            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17226            assert_eq!(
17227                highlighted_edits.highlights[0].1.background_color,
17228                Some(cx.theme().status().created_background)
17229            );
17230            assert_eq!(
17231                highlighted_edits.highlights[1].1.background_color,
17232                Some(cx.theme().status().created_background)
17233            );
17234        },
17235    )
17236    .await;
17237
17238    // Multiple lines with edits
17239    assert_highlighted_edits(
17240        "First line\nSecond line\nThird line\nFourth line",
17241        vec![
17242            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17243            (
17244                Point::new(2, 0)..Point::new(2, 10),
17245                "New third line".to_string(),
17246            ),
17247            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17248        ],
17249        false,
17250        cx,
17251        |highlighted_edits, cx| {
17252            assert_eq!(
17253                highlighted_edits.text,
17254                "Second modified\nNew third line\nFourth updated line"
17255            );
17256            assert_eq!(highlighted_edits.highlights.len(), 3);
17257            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17258            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17259            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17260            for highlight in &highlighted_edits.highlights {
17261                assert_eq!(
17262                    highlight.1.background_color,
17263                    Some(cx.theme().status().created_background)
17264                );
17265            }
17266        },
17267    )
17268    .await;
17269}
17270
17271#[gpui::test]
17272async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17273    init_test(cx, |_| {});
17274
17275    // Deletion
17276    assert_highlighted_edits(
17277        "Hello, world!",
17278        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17279        true,
17280        cx,
17281        |highlighted_edits, cx| {
17282            assert_eq!(highlighted_edits.text, "Hello, world!");
17283            assert_eq!(highlighted_edits.highlights.len(), 1);
17284            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17285            assert_eq!(
17286                highlighted_edits.highlights[0].1.background_color,
17287                Some(cx.theme().status().deleted_background)
17288            );
17289        },
17290    )
17291    .await;
17292
17293    // Insertion
17294    assert_highlighted_edits(
17295        "Hello, world!",
17296        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17297        true,
17298        cx,
17299        |highlighted_edits, cx| {
17300            assert_eq!(highlighted_edits.highlights.len(), 1);
17301            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17302            assert_eq!(
17303                highlighted_edits.highlights[0].1.background_color,
17304                Some(cx.theme().status().created_background)
17305            );
17306        },
17307    )
17308    .await;
17309}
17310
17311async fn assert_highlighted_edits(
17312    text: &str,
17313    edits: Vec<(Range<Point>, String)>,
17314    include_deletions: bool,
17315    cx: &mut TestAppContext,
17316    assertion_fn: impl Fn(HighlightedText, &App),
17317) {
17318    let window = cx.add_window(|window, cx| {
17319        let buffer = MultiBuffer::build_simple(text, cx);
17320        Editor::new(EditorMode::Full, buffer, None, window, cx)
17321    });
17322    let cx = &mut VisualTestContext::from_window(*window, cx);
17323
17324    let (buffer, snapshot) = window
17325        .update(cx, |editor, _window, cx| {
17326            (
17327                editor.buffer().clone(),
17328                editor.buffer().read(cx).snapshot(cx),
17329            )
17330        })
17331        .unwrap();
17332
17333    let edits = edits
17334        .into_iter()
17335        .map(|(range, edit)| {
17336            (
17337                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17338                edit,
17339            )
17340        })
17341        .collect::<Vec<_>>();
17342
17343    let text_anchor_edits = edits
17344        .clone()
17345        .into_iter()
17346        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17347        .collect::<Vec<_>>();
17348
17349    let edit_preview = window
17350        .update(cx, |_, _window, cx| {
17351            buffer
17352                .read(cx)
17353                .as_singleton()
17354                .unwrap()
17355                .read(cx)
17356                .preview_edits(text_anchor_edits.into(), cx)
17357        })
17358        .unwrap()
17359        .await;
17360
17361    cx.update(|_window, cx| {
17362        let highlighted_edits = inline_completion_edit_text(
17363            &snapshot.as_singleton().unwrap().2,
17364            &edits,
17365            &edit_preview,
17366            include_deletions,
17367            cx,
17368        );
17369        assertion_fn(highlighted_edits, cx)
17370    });
17371}
17372
17373#[track_caller]
17374fn assert_breakpoint(
17375    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17376    path: &Arc<Path>,
17377    expected: Vec<(u32, Breakpoint)>,
17378) {
17379    if expected.len() == 0usize {
17380        assert!(!breakpoints.contains_key(path), "{}", path.display());
17381    } else {
17382        let mut breakpoint = breakpoints
17383            .get(path)
17384            .unwrap()
17385            .into_iter()
17386            .map(|breakpoint| {
17387                (
17388                    breakpoint.row,
17389                    Breakpoint {
17390                        message: breakpoint.message.clone(),
17391                        state: breakpoint.state,
17392                        condition: breakpoint.condition.clone(),
17393                        hit_condition: breakpoint.hit_condition.clone(),
17394                    },
17395                )
17396            })
17397            .collect::<Vec<_>>();
17398
17399        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17400
17401        assert_eq!(expected, breakpoint);
17402    }
17403}
17404
17405fn add_log_breakpoint_at_cursor(
17406    editor: &mut Editor,
17407    log_message: &str,
17408    window: &mut Window,
17409    cx: &mut Context<Editor>,
17410) {
17411    let (anchor, bp) = editor
17412        .breakpoint_at_cursor_head(window, cx)
17413        .unwrap_or_else(|| {
17414            let cursor_position: Point = editor.selections.newest(cx).head();
17415
17416            let breakpoint_position = editor
17417                .snapshot(window, cx)
17418                .display_snapshot
17419                .buffer_snapshot
17420                .anchor_before(Point::new(cursor_position.row, 0));
17421
17422            (breakpoint_position, Breakpoint::new_log(&log_message))
17423        });
17424
17425    editor.edit_breakpoint_at_anchor(
17426        anchor,
17427        bp,
17428        BreakpointEditAction::EditLogMessage(log_message.into()),
17429        cx,
17430    );
17431}
17432
17433#[gpui::test]
17434async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17435    init_test(cx, |_| {});
17436
17437    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17438    let fs = FakeFs::new(cx.executor());
17439    fs.insert_tree(
17440        path!("/a"),
17441        json!({
17442            "main.rs": sample_text,
17443        }),
17444    )
17445    .await;
17446    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17447    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17448    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17449
17450    let fs = FakeFs::new(cx.executor());
17451    fs.insert_tree(
17452        path!("/a"),
17453        json!({
17454            "main.rs": sample_text,
17455        }),
17456    )
17457    .await;
17458    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17459    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17460    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17461    let worktree_id = workspace
17462        .update(cx, |workspace, _window, cx| {
17463            workspace.project().update(cx, |project, cx| {
17464                project.worktrees(cx).next().unwrap().read(cx).id()
17465            })
17466        })
17467        .unwrap();
17468
17469    let buffer = project
17470        .update(cx, |project, cx| {
17471            project.open_buffer((worktree_id, "main.rs"), cx)
17472        })
17473        .await
17474        .unwrap();
17475
17476    let (editor, cx) = cx.add_window_view(|window, cx| {
17477        Editor::new(
17478            EditorMode::Full,
17479            MultiBuffer::build_from_buffer(buffer, cx),
17480            Some(project.clone()),
17481            window,
17482            cx,
17483        )
17484    });
17485
17486    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17487    let abs_path = project.read_with(cx, |project, cx| {
17488        project
17489            .absolute_path(&project_path, cx)
17490            .map(|path_buf| Arc::from(path_buf.to_owned()))
17491            .unwrap()
17492    });
17493
17494    // assert we can add breakpoint on the first line
17495    editor.update_in(cx, |editor, window, cx| {
17496        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17497        editor.move_to_end(&MoveToEnd, window, cx);
17498        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17499    });
17500
17501    let breakpoints = editor.update(cx, |editor, cx| {
17502        editor
17503            .breakpoint_store()
17504            .as_ref()
17505            .unwrap()
17506            .read(cx)
17507            .all_breakpoints(cx)
17508            .clone()
17509    });
17510
17511    assert_eq!(1, breakpoints.len());
17512    assert_breakpoint(
17513        &breakpoints,
17514        &abs_path,
17515        vec![
17516            (0, Breakpoint::new_standard()),
17517            (3, Breakpoint::new_standard()),
17518        ],
17519    );
17520
17521    editor.update_in(cx, |editor, window, cx| {
17522        editor.move_to_beginning(&MoveToBeginning, window, cx);
17523        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17524    });
17525
17526    let breakpoints = editor.update(cx, |editor, cx| {
17527        editor
17528            .breakpoint_store()
17529            .as_ref()
17530            .unwrap()
17531            .read(cx)
17532            .all_breakpoints(cx)
17533            .clone()
17534    });
17535
17536    assert_eq!(1, breakpoints.len());
17537    assert_breakpoint(
17538        &breakpoints,
17539        &abs_path,
17540        vec![(3, Breakpoint::new_standard())],
17541    );
17542
17543    editor.update_in(cx, |editor, window, cx| {
17544        editor.move_to_end(&MoveToEnd, window, cx);
17545        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17546    });
17547
17548    let breakpoints = editor.update(cx, |editor, cx| {
17549        editor
17550            .breakpoint_store()
17551            .as_ref()
17552            .unwrap()
17553            .read(cx)
17554            .all_breakpoints(cx)
17555            .clone()
17556    });
17557
17558    assert_eq!(0, breakpoints.len());
17559    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17560}
17561
17562#[gpui::test]
17563async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17564    init_test(cx, |_| {});
17565
17566    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17567
17568    let fs = FakeFs::new(cx.executor());
17569    fs.insert_tree(
17570        path!("/a"),
17571        json!({
17572            "main.rs": sample_text,
17573        }),
17574    )
17575    .await;
17576    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17577    let (workspace, cx) =
17578        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17579
17580    let worktree_id = workspace.update(cx, |workspace, cx| {
17581        workspace.project().update(cx, |project, cx| {
17582            project.worktrees(cx).next().unwrap().read(cx).id()
17583        })
17584    });
17585
17586    let buffer = project
17587        .update(cx, |project, cx| {
17588            project.open_buffer((worktree_id, "main.rs"), cx)
17589        })
17590        .await
17591        .unwrap();
17592
17593    let (editor, cx) = cx.add_window_view(|window, cx| {
17594        Editor::new(
17595            EditorMode::Full,
17596            MultiBuffer::build_from_buffer(buffer, cx),
17597            Some(project.clone()),
17598            window,
17599            cx,
17600        )
17601    });
17602
17603    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17604    let abs_path = project.read_with(cx, |project, cx| {
17605        project
17606            .absolute_path(&project_path, cx)
17607            .map(|path_buf| Arc::from(path_buf.to_owned()))
17608            .unwrap()
17609    });
17610
17611    editor.update_in(cx, |editor, window, cx| {
17612        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17613    });
17614
17615    let breakpoints = editor.update(cx, |editor, cx| {
17616        editor
17617            .breakpoint_store()
17618            .as_ref()
17619            .unwrap()
17620            .read(cx)
17621            .all_breakpoints(cx)
17622            .clone()
17623    });
17624
17625    assert_breakpoint(
17626        &breakpoints,
17627        &abs_path,
17628        vec![(0, Breakpoint::new_log("hello world"))],
17629    );
17630
17631    // Removing a log message from a log breakpoint should remove it
17632    editor.update_in(cx, |editor, window, cx| {
17633        add_log_breakpoint_at_cursor(editor, "", window, cx);
17634    });
17635
17636    let breakpoints = editor.update(cx, |editor, cx| {
17637        editor
17638            .breakpoint_store()
17639            .as_ref()
17640            .unwrap()
17641            .read(cx)
17642            .all_breakpoints(cx)
17643            .clone()
17644    });
17645
17646    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17647
17648    editor.update_in(cx, |editor, window, cx| {
17649        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17650        editor.move_to_end(&MoveToEnd, window, cx);
17651        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17652        // Not adding a log message to a standard breakpoint shouldn't remove it
17653        add_log_breakpoint_at_cursor(editor, "", window, cx);
17654    });
17655
17656    let breakpoints = editor.update(cx, |editor, cx| {
17657        editor
17658            .breakpoint_store()
17659            .as_ref()
17660            .unwrap()
17661            .read(cx)
17662            .all_breakpoints(cx)
17663            .clone()
17664    });
17665
17666    assert_breakpoint(
17667        &breakpoints,
17668        &abs_path,
17669        vec![
17670            (0, Breakpoint::new_standard()),
17671            (3, Breakpoint::new_standard()),
17672        ],
17673    );
17674
17675    editor.update_in(cx, |editor, window, cx| {
17676        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17677    });
17678
17679    let breakpoints = editor.update(cx, |editor, cx| {
17680        editor
17681            .breakpoint_store()
17682            .as_ref()
17683            .unwrap()
17684            .read(cx)
17685            .all_breakpoints(cx)
17686            .clone()
17687    });
17688
17689    assert_breakpoint(
17690        &breakpoints,
17691        &abs_path,
17692        vec![
17693            (0, Breakpoint::new_standard()),
17694            (3, Breakpoint::new_log("hello world")),
17695        ],
17696    );
17697
17698    editor.update_in(cx, |editor, window, cx| {
17699        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17700    });
17701
17702    let breakpoints = editor.update(cx, |editor, cx| {
17703        editor
17704            .breakpoint_store()
17705            .as_ref()
17706            .unwrap()
17707            .read(cx)
17708            .all_breakpoints(cx)
17709            .clone()
17710    });
17711
17712    assert_breakpoint(
17713        &breakpoints,
17714        &abs_path,
17715        vec![
17716            (0, Breakpoint::new_standard()),
17717            (3, Breakpoint::new_log("hello Earth!!")),
17718        ],
17719    );
17720}
17721
17722/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17723/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17724/// or when breakpoints were placed out of order. This tests for a regression too
17725#[gpui::test]
17726async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17727    init_test(cx, |_| {});
17728
17729    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17730    let fs = FakeFs::new(cx.executor());
17731    fs.insert_tree(
17732        path!("/a"),
17733        json!({
17734            "main.rs": sample_text,
17735        }),
17736    )
17737    .await;
17738    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17739    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17740    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17741
17742    let fs = FakeFs::new(cx.executor());
17743    fs.insert_tree(
17744        path!("/a"),
17745        json!({
17746            "main.rs": sample_text,
17747        }),
17748    )
17749    .await;
17750    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17751    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17752    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17753    let worktree_id = workspace
17754        .update(cx, |workspace, _window, cx| {
17755            workspace.project().update(cx, |project, cx| {
17756                project.worktrees(cx).next().unwrap().read(cx).id()
17757            })
17758        })
17759        .unwrap();
17760
17761    let buffer = project
17762        .update(cx, |project, cx| {
17763            project.open_buffer((worktree_id, "main.rs"), cx)
17764        })
17765        .await
17766        .unwrap();
17767
17768    let (editor, cx) = cx.add_window_view(|window, cx| {
17769        Editor::new(
17770            EditorMode::Full,
17771            MultiBuffer::build_from_buffer(buffer, cx),
17772            Some(project.clone()),
17773            window,
17774            cx,
17775        )
17776    });
17777
17778    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17779    let abs_path = project.read_with(cx, |project, cx| {
17780        project
17781            .absolute_path(&project_path, cx)
17782            .map(|path_buf| Arc::from(path_buf.to_owned()))
17783            .unwrap()
17784    });
17785
17786    // assert we can add breakpoint on the first line
17787    editor.update_in(cx, |editor, window, cx| {
17788        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17789        editor.move_to_end(&MoveToEnd, window, cx);
17790        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17791        editor.move_up(&MoveUp, window, cx);
17792        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17793    });
17794
17795    let breakpoints = editor.update(cx, |editor, cx| {
17796        editor
17797            .breakpoint_store()
17798            .as_ref()
17799            .unwrap()
17800            .read(cx)
17801            .all_breakpoints(cx)
17802            .clone()
17803    });
17804
17805    assert_eq!(1, breakpoints.len());
17806    assert_breakpoint(
17807        &breakpoints,
17808        &abs_path,
17809        vec![
17810            (0, Breakpoint::new_standard()),
17811            (2, Breakpoint::new_standard()),
17812            (3, Breakpoint::new_standard()),
17813        ],
17814    );
17815
17816    editor.update_in(cx, |editor, window, cx| {
17817        editor.move_to_beginning(&MoveToBeginning, window, cx);
17818        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17819        editor.move_to_end(&MoveToEnd, window, cx);
17820        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17821        // Disabling a breakpoint that doesn't exist should do nothing
17822        editor.move_up(&MoveUp, window, cx);
17823        editor.move_up(&MoveUp, window, cx);
17824        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17825    });
17826
17827    let breakpoints = editor.update(cx, |editor, cx| {
17828        editor
17829            .breakpoint_store()
17830            .as_ref()
17831            .unwrap()
17832            .read(cx)
17833            .all_breakpoints(cx)
17834            .clone()
17835    });
17836
17837    let disable_breakpoint = {
17838        let mut bp = Breakpoint::new_standard();
17839        bp.state = BreakpointState::Disabled;
17840        bp
17841    };
17842
17843    assert_eq!(1, breakpoints.len());
17844    assert_breakpoint(
17845        &breakpoints,
17846        &abs_path,
17847        vec![
17848            (0, disable_breakpoint.clone()),
17849            (2, Breakpoint::new_standard()),
17850            (3, disable_breakpoint.clone()),
17851        ],
17852    );
17853
17854    editor.update_in(cx, |editor, window, cx| {
17855        editor.move_to_beginning(&MoveToBeginning, window, cx);
17856        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17857        editor.move_to_end(&MoveToEnd, window, cx);
17858        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17859        editor.move_up(&MoveUp, window, cx);
17860        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17861    });
17862
17863    let breakpoints = editor.update(cx, |editor, cx| {
17864        editor
17865            .breakpoint_store()
17866            .as_ref()
17867            .unwrap()
17868            .read(cx)
17869            .all_breakpoints(cx)
17870            .clone()
17871    });
17872
17873    assert_eq!(1, breakpoints.len());
17874    assert_breakpoint(
17875        &breakpoints,
17876        &abs_path,
17877        vec![
17878            (0, Breakpoint::new_standard()),
17879            (2, disable_breakpoint),
17880            (3, Breakpoint::new_standard()),
17881        ],
17882    );
17883}
17884
17885#[gpui::test]
17886async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17887    init_test(cx, |_| {});
17888    let capabilities = lsp::ServerCapabilities {
17889        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17890            prepare_provider: Some(true),
17891            work_done_progress_options: Default::default(),
17892        })),
17893        ..Default::default()
17894    };
17895    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17896
17897    cx.set_state(indoc! {"
17898        struct Fˇoo {}
17899    "});
17900
17901    cx.update_editor(|editor, _, cx| {
17902        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17903        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17904        editor.highlight_background::<DocumentHighlightRead>(
17905            &[highlight_range],
17906            |c| c.editor_document_highlight_read_background,
17907            cx,
17908        );
17909    });
17910
17911    let mut prepare_rename_handler = cx
17912        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17913            move |_, _, _| async move {
17914                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17915                    start: lsp::Position {
17916                        line: 0,
17917                        character: 7,
17918                    },
17919                    end: lsp::Position {
17920                        line: 0,
17921                        character: 10,
17922                    },
17923                })))
17924            },
17925        );
17926    let prepare_rename_task = cx
17927        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17928        .expect("Prepare rename was not started");
17929    prepare_rename_handler.next().await.unwrap();
17930    prepare_rename_task.await.expect("Prepare rename failed");
17931
17932    let mut rename_handler =
17933        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17934            let edit = lsp::TextEdit {
17935                range: lsp::Range {
17936                    start: lsp::Position {
17937                        line: 0,
17938                        character: 7,
17939                    },
17940                    end: lsp::Position {
17941                        line: 0,
17942                        character: 10,
17943                    },
17944                },
17945                new_text: "FooRenamed".to_string(),
17946            };
17947            Ok(Some(lsp::WorkspaceEdit::new(
17948                // Specify the same edit twice
17949                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17950            )))
17951        });
17952    let rename_task = cx
17953        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17954        .expect("Confirm rename was not started");
17955    rename_handler.next().await.unwrap();
17956    rename_task.await.expect("Confirm rename failed");
17957    cx.run_until_parked();
17958
17959    // Despite two edits, only one is actually applied as those are identical
17960    cx.assert_editor_state(indoc! {"
17961        struct FooRenamedˇ {}
17962    "});
17963}
17964
17965#[gpui::test]
17966async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17967    init_test(cx, |_| {});
17968    // These capabilities indicate that the server does not support prepare rename.
17969    let capabilities = lsp::ServerCapabilities {
17970        rename_provider: Some(lsp::OneOf::Left(true)),
17971        ..Default::default()
17972    };
17973    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17974
17975    cx.set_state(indoc! {"
17976        struct Fˇoo {}
17977    "});
17978
17979    cx.update_editor(|editor, _window, cx| {
17980        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17981        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17982        editor.highlight_background::<DocumentHighlightRead>(
17983            &[highlight_range],
17984            |c| c.editor_document_highlight_read_background,
17985            cx,
17986        );
17987    });
17988
17989    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17990        .expect("Prepare rename was not started")
17991        .await
17992        .expect("Prepare rename failed");
17993
17994    let mut rename_handler =
17995        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17996            let edit = lsp::TextEdit {
17997                range: lsp::Range {
17998                    start: lsp::Position {
17999                        line: 0,
18000                        character: 7,
18001                    },
18002                    end: lsp::Position {
18003                        line: 0,
18004                        character: 10,
18005                    },
18006                },
18007                new_text: "FooRenamed".to_string(),
18008            };
18009            Ok(Some(lsp::WorkspaceEdit::new(
18010                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18011            )))
18012        });
18013    let rename_task = cx
18014        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18015        .expect("Confirm rename was not started");
18016    rename_handler.next().await.unwrap();
18017    rename_task.await.expect("Confirm rename failed");
18018    cx.run_until_parked();
18019
18020    // Correct range is renamed, as `surrounding_word` is used to find it.
18021    cx.assert_editor_state(indoc! {"
18022        struct FooRenamedˇ {}
18023    "});
18024}
18025
18026#[gpui::test]
18027async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18028    init_test(cx, |_| {});
18029    let mut cx = EditorTestContext::new(cx).await;
18030
18031    let language = Arc::new(
18032        Language::new(
18033            LanguageConfig::default(),
18034            Some(tree_sitter_html::LANGUAGE.into()),
18035        )
18036        .with_brackets_query(
18037            r#"
18038            ("<" @open "/>" @close)
18039            ("</" @open ">" @close)
18040            ("<" @open ">" @close)
18041            ("\"" @open "\"" @close)
18042            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18043        "#,
18044        )
18045        .unwrap(),
18046    );
18047    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18048
18049    cx.set_state(indoc! {"
18050        <span>ˇ</span>
18051    "});
18052    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18053    cx.assert_editor_state(indoc! {"
18054        <span>
18055        ˇ
18056        </span>
18057    "});
18058
18059    cx.set_state(indoc! {"
18060        <span><span></span>ˇ</span>
18061    "});
18062    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18063    cx.assert_editor_state(indoc! {"
18064        <span><span></span>
18065        ˇ</span>
18066    "});
18067
18068    cx.set_state(indoc! {"
18069        <span>ˇ
18070        </span>
18071    "});
18072    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18073    cx.assert_editor_state(indoc! {"
18074        <span>
18075        ˇ
18076        </span>
18077    "});
18078}
18079
18080#[gpui::test(iterations = 10)]
18081async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18082    init_test(cx, |_| {});
18083
18084    let fs = FakeFs::new(cx.executor());
18085    fs.insert_tree(
18086        path!("/dir"),
18087        json!({
18088            "a.ts": "a",
18089        }),
18090    )
18091    .await;
18092
18093    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18094    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18095    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18096
18097    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18098    language_registry.add(Arc::new(Language::new(
18099        LanguageConfig {
18100            name: "TypeScript".into(),
18101            matcher: LanguageMatcher {
18102                path_suffixes: vec!["ts".to_string()],
18103                ..Default::default()
18104            },
18105            ..Default::default()
18106        },
18107        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18108    )));
18109    let mut fake_language_servers = language_registry.register_fake_lsp(
18110        "TypeScript",
18111        FakeLspAdapter {
18112            capabilities: lsp::ServerCapabilities {
18113                code_lens_provider: Some(lsp::CodeLensOptions {
18114                    resolve_provider: Some(true),
18115                }),
18116                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18117                    commands: vec!["_the/command".to_string()],
18118                    ..lsp::ExecuteCommandOptions::default()
18119                }),
18120                ..lsp::ServerCapabilities::default()
18121            },
18122            ..FakeLspAdapter::default()
18123        },
18124    );
18125
18126    let (buffer, _handle) = project
18127        .update(cx, |p, cx| {
18128            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18129        })
18130        .await
18131        .unwrap();
18132    cx.executor().run_until_parked();
18133
18134    let fake_server = fake_language_servers.next().await.unwrap();
18135
18136    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18137    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18138    drop(buffer_snapshot);
18139    let actions = cx
18140        .update_window(*workspace, |_, window, cx| {
18141            project.code_actions(&buffer, anchor..anchor, window, cx)
18142        })
18143        .unwrap();
18144
18145    fake_server
18146        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18147            Ok(Some(vec![
18148                lsp::CodeLens {
18149                    range: lsp::Range::default(),
18150                    command: Some(lsp::Command {
18151                        title: "Code lens command".to_owned(),
18152                        command: "_the/command".to_owned(),
18153                        arguments: None,
18154                    }),
18155                    data: None,
18156                },
18157                lsp::CodeLens {
18158                    range: lsp::Range::default(),
18159                    command: Some(lsp::Command {
18160                        title: "Command not in capabilities".to_owned(),
18161                        command: "not in capabilities".to_owned(),
18162                        arguments: None,
18163                    }),
18164                    data: None,
18165                },
18166                lsp::CodeLens {
18167                    range: lsp::Range {
18168                        start: lsp::Position {
18169                            line: 1,
18170                            character: 1,
18171                        },
18172                        end: lsp::Position {
18173                            line: 1,
18174                            character: 1,
18175                        },
18176                    },
18177                    command: Some(lsp::Command {
18178                        title: "Command not in range".to_owned(),
18179                        command: "_the/command".to_owned(),
18180                        arguments: None,
18181                    }),
18182                    data: None,
18183                },
18184            ]))
18185        })
18186        .next()
18187        .await;
18188
18189    let actions = actions.await.unwrap();
18190    assert_eq!(
18191        actions.len(),
18192        1,
18193        "Should have only one valid action for the 0..0 range"
18194    );
18195    let action = actions[0].clone();
18196    let apply = project.update(cx, |project, cx| {
18197        project.apply_code_action(buffer.clone(), action, true, cx)
18198    });
18199
18200    // Resolving the code action does not populate its edits. In absence of
18201    // edits, we must execute the given command.
18202    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18203        |mut lens, _| async move {
18204            let lens_command = lens.command.as_mut().expect("should have a command");
18205            assert_eq!(lens_command.title, "Code lens command");
18206            lens_command.arguments = Some(vec![json!("the-argument")]);
18207            Ok(lens)
18208        },
18209    );
18210
18211    // While executing the command, the language server sends the editor
18212    // a `workspaceEdit` request.
18213    fake_server
18214        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18215            let fake = fake_server.clone();
18216            move |params, _| {
18217                assert_eq!(params.command, "_the/command");
18218                let fake = fake.clone();
18219                async move {
18220                    fake.server
18221                        .request::<lsp::request::ApplyWorkspaceEdit>(
18222                            lsp::ApplyWorkspaceEditParams {
18223                                label: None,
18224                                edit: lsp::WorkspaceEdit {
18225                                    changes: Some(
18226                                        [(
18227                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18228                                            vec![lsp::TextEdit {
18229                                                range: lsp::Range::new(
18230                                                    lsp::Position::new(0, 0),
18231                                                    lsp::Position::new(0, 0),
18232                                                ),
18233                                                new_text: "X".into(),
18234                                            }],
18235                                        )]
18236                                        .into_iter()
18237                                        .collect(),
18238                                    ),
18239                                    ..Default::default()
18240                                },
18241                            },
18242                        )
18243                        .await
18244                        .unwrap();
18245                    Ok(Some(json!(null)))
18246                }
18247            }
18248        })
18249        .next()
18250        .await;
18251
18252    // Applying the code lens command returns a project transaction containing the edits
18253    // sent by the language server in its `workspaceEdit` request.
18254    let transaction = apply.await.unwrap();
18255    assert!(transaction.0.contains_key(&buffer));
18256    buffer.update(cx, |buffer, cx| {
18257        assert_eq!(buffer.text(), "Xa");
18258        buffer.undo(cx);
18259        assert_eq!(buffer.text(), "a");
18260    });
18261}
18262
18263#[gpui::test]
18264async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18265    init_test(cx, |_| {});
18266
18267    let fs = FakeFs::new(cx.executor());
18268    let main_text = r#"fn main() {
18269println!("1");
18270println!("2");
18271println!("3");
18272println!("4");
18273println!("5");
18274}"#;
18275    let lib_text = "mod foo {}";
18276    fs.insert_tree(
18277        path!("/a"),
18278        json!({
18279            "lib.rs": lib_text,
18280            "main.rs": main_text,
18281        }),
18282    )
18283    .await;
18284
18285    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18286    let (workspace, cx) =
18287        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18288    let worktree_id = workspace.update(cx, |workspace, cx| {
18289        workspace.project().update(cx, |project, cx| {
18290            project.worktrees(cx).next().unwrap().read(cx).id()
18291        })
18292    });
18293
18294    let expected_ranges = vec![
18295        Point::new(0, 0)..Point::new(0, 0),
18296        Point::new(1, 0)..Point::new(1, 1),
18297        Point::new(2, 0)..Point::new(2, 2),
18298        Point::new(3, 0)..Point::new(3, 3),
18299    ];
18300
18301    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18302    let editor_1 = workspace
18303        .update_in(cx, |workspace, window, cx| {
18304            workspace.open_path(
18305                (worktree_id, "main.rs"),
18306                Some(pane_1.downgrade()),
18307                true,
18308                window,
18309                cx,
18310            )
18311        })
18312        .unwrap()
18313        .await
18314        .downcast::<Editor>()
18315        .unwrap();
18316    pane_1.update(cx, |pane, cx| {
18317        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18318        open_editor.update(cx, |editor, cx| {
18319            assert_eq!(
18320                editor.display_text(cx),
18321                main_text,
18322                "Original main.rs text on initial open",
18323            );
18324            assert_eq!(
18325                editor
18326                    .selections
18327                    .all::<Point>(cx)
18328                    .into_iter()
18329                    .map(|s| s.range())
18330                    .collect::<Vec<_>>(),
18331                vec![Point::zero()..Point::zero()],
18332                "Default selections on initial open",
18333            );
18334        })
18335    });
18336    editor_1.update_in(cx, |editor, window, cx| {
18337        editor.change_selections(None, window, cx, |s| {
18338            s.select_ranges(expected_ranges.clone());
18339        });
18340    });
18341
18342    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18343        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18344    });
18345    let editor_2 = workspace
18346        .update_in(cx, |workspace, window, cx| {
18347            workspace.open_path(
18348                (worktree_id, "main.rs"),
18349                Some(pane_2.downgrade()),
18350                true,
18351                window,
18352                cx,
18353            )
18354        })
18355        .unwrap()
18356        .await
18357        .downcast::<Editor>()
18358        .unwrap();
18359    pane_2.update(cx, |pane, cx| {
18360        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18361        open_editor.update(cx, |editor, cx| {
18362            assert_eq!(
18363                editor.display_text(cx),
18364                main_text,
18365                "Original main.rs text on initial open in another panel",
18366            );
18367            assert_eq!(
18368                editor
18369                    .selections
18370                    .all::<Point>(cx)
18371                    .into_iter()
18372                    .map(|s| s.range())
18373                    .collect::<Vec<_>>(),
18374                vec![Point::zero()..Point::zero()],
18375                "Default selections on initial open in another panel",
18376            );
18377        })
18378    });
18379
18380    editor_2.update_in(cx, |editor, window, cx| {
18381        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18382    });
18383
18384    let _other_editor_1 = workspace
18385        .update_in(cx, |workspace, window, cx| {
18386            workspace.open_path(
18387                (worktree_id, "lib.rs"),
18388                Some(pane_1.downgrade()),
18389                true,
18390                window,
18391                cx,
18392            )
18393        })
18394        .unwrap()
18395        .await
18396        .downcast::<Editor>()
18397        .unwrap();
18398    pane_1
18399        .update_in(cx, |pane, window, cx| {
18400            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18401                .unwrap()
18402        })
18403        .await
18404        .unwrap();
18405    drop(editor_1);
18406    pane_1.update(cx, |pane, cx| {
18407        pane.active_item()
18408            .unwrap()
18409            .downcast::<Editor>()
18410            .unwrap()
18411            .update(cx, |editor, cx| {
18412                assert_eq!(
18413                    editor.display_text(cx),
18414                    lib_text,
18415                    "Other file should be open and active",
18416                );
18417            });
18418        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18419    });
18420
18421    let _other_editor_2 = workspace
18422        .update_in(cx, |workspace, window, cx| {
18423            workspace.open_path(
18424                (worktree_id, "lib.rs"),
18425                Some(pane_2.downgrade()),
18426                true,
18427                window,
18428                cx,
18429            )
18430        })
18431        .unwrap()
18432        .await
18433        .downcast::<Editor>()
18434        .unwrap();
18435    pane_2
18436        .update_in(cx, |pane, window, cx| {
18437            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18438                .unwrap()
18439        })
18440        .await
18441        .unwrap();
18442    drop(editor_2);
18443    pane_2.update(cx, |pane, cx| {
18444        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18445        open_editor.update(cx, |editor, cx| {
18446            assert_eq!(
18447                editor.display_text(cx),
18448                lib_text,
18449                "Other file should be open and active in another panel too",
18450            );
18451        });
18452        assert_eq!(
18453            pane.items().count(),
18454            1,
18455            "No other editors should be open in another pane",
18456        );
18457    });
18458
18459    let _editor_1_reopened = workspace
18460        .update_in(cx, |workspace, window, cx| {
18461            workspace.open_path(
18462                (worktree_id, "main.rs"),
18463                Some(pane_1.downgrade()),
18464                true,
18465                window,
18466                cx,
18467            )
18468        })
18469        .unwrap()
18470        .await
18471        .downcast::<Editor>()
18472        .unwrap();
18473    let _editor_2_reopened = workspace
18474        .update_in(cx, |workspace, window, cx| {
18475            workspace.open_path(
18476                (worktree_id, "main.rs"),
18477                Some(pane_2.downgrade()),
18478                true,
18479                window,
18480                cx,
18481            )
18482        })
18483        .unwrap()
18484        .await
18485        .downcast::<Editor>()
18486        .unwrap();
18487    pane_1.update(cx, |pane, cx| {
18488        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18489        open_editor.update(cx, |editor, cx| {
18490            assert_eq!(
18491                editor.display_text(cx),
18492                main_text,
18493                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18494            );
18495            assert_eq!(
18496                editor
18497                    .selections
18498                    .all::<Point>(cx)
18499                    .into_iter()
18500                    .map(|s| s.range())
18501                    .collect::<Vec<_>>(),
18502                expected_ranges,
18503                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18504            );
18505        })
18506    });
18507    pane_2.update(cx, |pane, cx| {
18508        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18509        open_editor.update(cx, |editor, cx| {
18510            assert_eq!(
18511                editor.display_text(cx),
18512                r#"fn main() {
18513⋯rintln!("1");
18514⋯intln!("2");
18515⋯ntln!("3");
18516println!("4");
18517println!("5");
18518}"#,
18519                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18520            );
18521            assert_eq!(
18522                editor
18523                    .selections
18524                    .all::<Point>(cx)
18525                    .into_iter()
18526                    .map(|s| s.range())
18527                    .collect::<Vec<_>>(),
18528                vec![Point::zero()..Point::zero()],
18529                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18530            );
18531        })
18532    });
18533}
18534
18535#[gpui::test]
18536async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18537    init_test(cx, |_| {});
18538
18539    let fs = FakeFs::new(cx.executor());
18540    let main_text = r#"fn main() {
18541println!("1");
18542println!("2");
18543println!("3");
18544println!("4");
18545println!("5");
18546}"#;
18547    let lib_text = "mod foo {}";
18548    fs.insert_tree(
18549        path!("/a"),
18550        json!({
18551            "lib.rs": lib_text,
18552            "main.rs": main_text,
18553        }),
18554    )
18555    .await;
18556
18557    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18558    let (workspace, cx) =
18559        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18560    let worktree_id = workspace.update(cx, |workspace, cx| {
18561        workspace.project().update(cx, |project, cx| {
18562            project.worktrees(cx).next().unwrap().read(cx).id()
18563        })
18564    });
18565
18566    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18567    let editor = workspace
18568        .update_in(cx, |workspace, window, cx| {
18569            workspace.open_path(
18570                (worktree_id, "main.rs"),
18571                Some(pane.downgrade()),
18572                true,
18573                window,
18574                cx,
18575            )
18576        })
18577        .unwrap()
18578        .await
18579        .downcast::<Editor>()
18580        .unwrap();
18581    pane.update(cx, |pane, cx| {
18582        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18583        open_editor.update(cx, |editor, cx| {
18584            assert_eq!(
18585                editor.display_text(cx),
18586                main_text,
18587                "Original main.rs text on initial open",
18588            );
18589        })
18590    });
18591    editor.update_in(cx, |editor, window, cx| {
18592        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18593    });
18594
18595    cx.update_global(|store: &mut SettingsStore, cx| {
18596        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18597            s.restore_on_file_reopen = Some(false);
18598        });
18599    });
18600    editor.update_in(cx, |editor, window, cx| {
18601        editor.fold_ranges(
18602            vec![
18603                Point::new(1, 0)..Point::new(1, 1),
18604                Point::new(2, 0)..Point::new(2, 2),
18605                Point::new(3, 0)..Point::new(3, 3),
18606            ],
18607            false,
18608            window,
18609            cx,
18610        );
18611    });
18612    pane.update_in(cx, |pane, window, cx| {
18613        pane.close_all_items(&CloseAllItems::default(), window, cx)
18614            .unwrap()
18615    })
18616    .await
18617    .unwrap();
18618    pane.update(cx, |pane, _| {
18619        assert!(pane.active_item().is_none());
18620    });
18621    cx.update_global(|store: &mut SettingsStore, cx| {
18622        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18623            s.restore_on_file_reopen = Some(true);
18624        });
18625    });
18626
18627    let _editor_reopened = 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                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18648            );
18649        })
18650    });
18651}
18652
18653fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18654    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18655    point..point
18656}
18657
18658fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18659    let (text, ranges) = marked_text_ranges(marked_text, true);
18660    assert_eq!(editor.text(cx), text);
18661    assert_eq!(
18662        editor.selections.ranges(cx),
18663        ranges,
18664        "Assert selections are {}",
18665        marked_text
18666    );
18667}
18668
18669pub fn handle_signature_help_request(
18670    cx: &mut EditorLspTestContext,
18671    mocked_response: lsp::SignatureHelp,
18672) -> impl Future<Output = ()> + use<> {
18673    let mut request =
18674        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18675            let mocked_response = mocked_response.clone();
18676            async move { Ok(Some(mocked_response)) }
18677        });
18678
18679    async move {
18680        request.next().await;
18681    }
18682}
18683
18684/// Handle completion request passing a marked string specifying where the completion
18685/// should be triggered from using '|' character, what range should be replaced, and what completions
18686/// should be returned using '<' and '>' to delimit the range.
18687///
18688/// Also see `handle_completion_request_with_insert_and_replace`.
18689#[track_caller]
18690pub fn handle_completion_request(
18691    cx: &mut EditorLspTestContext,
18692    marked_string: &str,
18693    completions: Vec<&'static str>,
18694    counter: Arc<AtomicUsize>,
18695) -> impl Future<Output = ()> {
18696    let complete_from_marker: TextRangeMarker = '|'.into();
18697    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18698    let (_, mut marked_ranges) = marked_text_ranges_by(
18699        marked_string,
18700        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18701    );
18702
18703    let complete_from_position =
18704        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18705    let replace_range =
18706        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18707
18708    let mut request =
18709        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18710            let completions = completions.clone();
18711            counter.fetch_add(1, atomic::Ordering::Release);
18712            async move {
18713                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18714                assert_eq!(
18715                    params.text_document_position.position,
18716                    complete_from_position
18717                );
18718                Ok(Some(lsp::CompletionResponse::Array(
18719                    completions
18720                        .iter()
18721                        .map(|completion_text| lsp::CompletionItem {
18722                            label: completion_text.to_string(),
18723                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18724                                range: replace_range,
18725                                new_text: completion_text.to_string(),
18726                            })),
18727                            ..Default::default()
18728                        })
18729                        .collect(),
18730                )))
18731            }
18732        });
18733
18734    async move {
18735        request.next().await;
18736    }
18737}
18738
18739/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18740/// given instead, which also contains an `insert` range.
18741///
18742/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18743/// that is, `replace_range.start..cursor_pos`.
18744pub fn handle_completion_request_with_insert_and_replace(
18745    cx: &mut EditorLspTestContext,
18746    marked_string: &str,
18747    completions: Vec<&'static str>,
18748    counter: Arc<AtomicUsize>,
18749) -> impl Future<Output = ()> {
18750    let complete_from_marker: TextRangeMarker = '|'.into();
18751    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18752    let (_, mut marked_ranges) = marked_text_ranges_by(
18753        marked_string,
18754        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18755    );
18756
18757    let complete_from_position =
18758        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18759    let replace_range =
18760        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18761
18762    let mut request =
18763        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18764            let completions = completions.clone();
18765            counter.fetch_add(1, atomic::Ordering::Release);
18766            async move {
18767                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18768                assert_eq!(
18769                    params.text_document_position.position, complete_from_position,
18770                    "marker `|` position doesn't match",
18771                );
18772                Ok(Some(lsp::CompletionResponse::Array(
18773                    completions
18774                        .iter()
18775                        .map(|completion_text| lsp::CompletionItem {
18776                            label: completion_text.to_string(),
18777                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18778                                lsp::InsertReplaceEdit {
18779                                    insert: lsp::Range {
18780                                        start: replace_range.start,
18781                                        end: complete_from_position,
18782                                    },
18783                                    replace: replace_range,
18784                                    new_text: completion_text.to_string(),
18785                                },
18786                            )),
18787                            ..Default::default()
18788                        })
18789                        .collect(),
18790                )))
18791            }
18792        });
18793
18794    async move {
18795        request.next().await;
18796    }
18797}
18798
18799fn handle_resolve_completion_request(
18800    cx: &mut EditorLspTestContext,
18801    edits: Option<Vec<(&'static str, &'static str)>>,
18802) -> impl Future<Output = ()> {
18803    let edits = edits.map(|edits| {
18804        edits
18805            .iter()
18806            .map(|(marked_string, new_text)| {
18807                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18808                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18809                lsp::TextEdit::new(replace_range, new_text.to_string())
18810            })
18811            .collect::<Vec<_>>()
18812    });
18813
18814    let mut request =
18815        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18816            let edits = edits.clone();
18817            async move {
18818                Ok(lsp::CompletionItem {
18819                    additional_text_edits: edits,
18820                    ..Default::default()
18821                })
18822            }
18823        });
18824
18825    async move {
18826        request.next().await;
18827    }
18828}
18829
18830pub(crate) fn update_test_language_settings(
18831    cx: &mut TestAppContext,
18832    f: impl Fn(&mut AllLanguageSettingsContent),
18833) {
18834    cx.update(|cx| {
18835        SettingsStore::update_global(cx, |store, cx| {
18836            store.update_user_settings::<AllLanguageSettings>(cx, f);
18837        });
18838    });
18839}
18840
18841pub(crate) fn update_test_project_settings(
18842    cx: &mut TestAppContext,
18843    f: impl Fn(&mut ProjectSettings),
18844) {
18845    cx.update(|cx| {
18846        SettingsStore::update_global(cx, |store, cx| {
18847            store.update_user_settings::<ProjectSettings>(cx, f);
18848        });
18849    });
18850}
18851
18852pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18853    cx.update(|cx| {
18854        assets::Assets.load_test_fonts(cx);
18855        let store = SettingsStore::test(cx);
18856        cx.set_global(store);
18857        theme::init(theme::LoadThemes::JustBase, cx);
18858        release_channel::init(SemanticVersion::default(), cx);
18859        client::init_settings(cx);
18860        language::init(cx);
18861        Project::init_settings(cx);
18862        workspace::init_settings(cx);
18863        crate::init(cx);
18864    });
18865
18866    update_test_language_settings(cx, f);
18867}
18868
18869#[track_caller]
18870fn assert_hunk_revert(
18871    not_reverted_text_with_selections: &str,
18872    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18873    expected_reverted_text_with_selections: &str,
18874    base_text: &str,
18875    cx: &mut EditorLspTestContext,
18876) {
18877    cx.set_state(not_reverted_text_with_selections);
18878    cx.set_head_text(base_text);
18879    cx.executor().run_until_parked();
18880
18881    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18882        let snapshot = editor.snapshot(window, cx);
18883        let reverted_hunk_statuses = snapshot
18884            .buffer_snapshot
18885            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18886            .map(|hunk| hunk.status().kind)
18887            .collect::<Vec<_>>();
18888
18889        editor.git_restore(&Default::default(), window, cx);
18890        reverted_hunk_statuses
18891    });
18892    cx.executor().run_until_parked();
18893    cx.assert_editor_state(expected_reverted_text_with_selections);
18894    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18895}