editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::IndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45};
   46use workspace::{
   47    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   48    NavigationEntry, ViewId,
   49};
   50
   51#[gpui::test]
   52fn test_edit_events(cx: &mut TestAppContext) {
   53    init_test(cx, |_| {});
   54
   55    let buffer = cx.new(|cx| {
   56        let mut buffer = language::Buffer::local("123456", cx);
   57        buffer.set_group_interval(Duration::from_secs(1));
   58        buffer
   59    });
   60
   61    let events = Rc::new(RefCell::new(Vec::new()));
   62    let editor1 = cx.add_window({
   63        let events = events.clone();
   64        |window, cx| {
   65            let model = cx.entity().clone();
   66            cx.subscribe_in(
   67                &model,
   68                window,
   69                move |_, _, event: &EditorEvent, _, _| match event {
   70                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   71                    EditorEvent::BufferEdited => {
   72                        events.borrow_mut().push(("editor1", "buffer edited"))
   73                    }
   74                    _ => {}
   75                },
   76            )
   77            .detach();
   78            Editor::for_buffer(buffer.clone(), None, window, cx)
   79        }
   80    });
   81
   82    let editor2 = cx.add_window({
   83        let events = events.clone();
   84        |window, cx| {
   85            cx.subscribe_in(
   86                &cx.entity().clone(),
   87                window,
   88                move |_, _, event: &EditorEvent, _, _| match event {
   89                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   90                    EditorEvent::BufferEdited => {
   91                        events.borrow_mut().push(("editor2", "buffer edited"))
   92                    }
   93                    _ => {}
   94                },
   95            )
   96            .detach();
   97            Editor::for_buffer(buffer.clone(), None, window, cx)
   98        }
   99    });
  100
  101    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  102
  103    // Mutating editor 1 will emit an `Edited` event only for that editor.
  104    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor1", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Mutating editor 2 will emit an `Edited` event only for that editor.
  115    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor2", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  137    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor1", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  159    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  160    assert_eq!(
  161        mem::take(&mut *events.borrow_mut()),
  162        [
  163            ("editor2", "edited"),
  164            ("editor1", "buffer edited"),
  165            ("editor2", "buffer edited"),
  166        ]
  167    );
  168
  169    // No event is emitted when the mutation is a no-op.
  170    _ = editor2.update(cx, |editor, window, cx| {
  171        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  172
  173        editor.backspace(&Backspace, window, cx);
  174    });
  175    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  176}
  177
  178#[gpui::test]
  179fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  180    init_test(cx, |_| {});
  181
  182    let mut now = Instant::now();
  183    let group_interval = Duration::from_millis(1);
  184    let buffer = cx.new(|cx| {
  185        let mut buf = language::Buffer::local("123456", cx);
  186        buf.set_group_interval(group_interval);
  187        buf
  188    });
  189    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  190    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  191
  192    _ = editor.update(cx, |editor, window, cx| {
  193        editor.start_transaction_at(now, window, cx);
  194        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  195
  196        editor.insert("cd", window, cx);
  197        editor.end_transaction_at(now, cx);
  198        assert_eq!(editor.text(cx), "12cd56");
  199        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  200
  201        editor.start_transaction_at(now, window, cx);
  202        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  203        editor.insert("e", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  207
  208        now += group_interval + Duration::from_millis(1);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  210
  211        // Simulate an edit in another editor
  212        buffer.update(cx, |buffer, cx| {
  213            buffer.start_transaction_at(now, cx);
  214            buffer.edit([(0..1, "a")], None, cx);
  215            buffer.edit([(1..1, "b")], None, cx);
  216            buffer.end_transaction_at(now, cx);
  217        });
  218
  219        assert_eq!(editor.text(cx), "ab2cde6");
  220        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  221
  222        // Last transaction happened past the group interval in a different editor.
  223        // Undo it individually and don't restore selections.
  224        editor.undo(&Undo, window, cx);
  225        assert_eq!(editor.text(cx), "12cde6");
  226        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  227
  228        // First two transactions happened within the group interval in this editor.
  229        // Undo them together and restore selections.
  230        editor.undo(&Undo, window, cx);
  231        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  232        assert_eq!(editor.text(cx), "123456");
  233        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  234
  235        // Redo the first two transactions together.
  236        editor.redo(&Redo, window, cx);
  237        assert_eq!(editor.text(cx), "12cde6");
  238        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  239
  240        // Redo the last transaction on its own.
  241        editor.redo(&Redo, window, cx);
  242        assert_eq!(editor.text(cx), "ab2cde6");
  243        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  244
  245        // Test empty transactions.
  246        editor.start_transaction_at(now, window, cx);
  247        editor.end_transaction_at(now, cx);
  248        editor.undo(&Undo, window, cx);
  249        assert_eq!(editor.text(cx), "12cde6");
  250    });
  251}
  252
  253#[gpui::test]
  254fn test_ime_composition(cx: &mut TestAppContext) {
  255    init_test(cx, |_| {});
  256
  257    let buffer = cx.new(|cx| {
  258        let mut buffer = language::Buffer::local("abcde", cx);
  259        // Ensure automatic grouping doesn't occur.
  260        buffer.set_group_interval(Duration::ZERO);
  261        buffer
  262    });
  263
  264    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  265    cx.add_window(|window, cx| {
  266        let mut editor = build_editor(buffer.clone(), window, cx);
  267
  268        // Start a new IME composition.
  269        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  270        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  271        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  272        assert_eq!(editor.text(cx), "äbcde");
  273        assert_eq!(
  274            editor.marked_text_ranges(cx),
  275            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  276        );
  277
  278        // Finalize IME composition.
  279        editor.replace_text_in_range(None, "ā", window, cx);
  280        assert_eq!(editor.text(cx), "ābcde");
  281        assert_eq!(editor.marked_text_ranges(cx), None);
  282
  283        // IME composition edits are grouped and are undone/redone at once.
  284        editor.undo(&Default::default(), window, cx);
  285        assert_eq!(editor.text(cx), "abcde");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287        editor.redo(&Default::default(), window, cx);
  288        assert_eq!(editor.text(cx), "ābcde");
  289        assert_eq!(editor.marked_text_ranges(cx), None);
  290
  291        // Start a new IME composition.
  292        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  293        assert_eq!(
  294            editor.marked_text_ranges(cx),
  295            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  296        );
  297
  298        // Undoing during an IME composition cancels it.
  299        editor.undo(&Default::default(), window, cx);
  300        assert_eq!(editor.text(cx), "ābcde");
  301        assert_eq!(editor.marked_text_ranges(cx), None);
  302
  303        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  304        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  305        assert_eq!(editor.text(cx), "ābcdè");
  306        assert_eq!(
  307            editor.marked_text_ranges(cx),
  308            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  309        );
  310
  311        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  312        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  313        assert_eq!(editor.text(cx), "ābcdę");
  314        assert_eq!(editor.marked_text_ranges(cx), None);
  315
  316        // Start a new IME composition with multiple cursors.
  317        editor.change_selections(None, window, cx, |s| {
  318            s.select_ranges([
  319                OffsetUtf16(1)..OffsetUtf16(1),
  320                OffsetUtf16(3)..OffsetUtf16(3),
  321                OffsetUtf16(5)..OffsetUtf16(5),
  322            ])
  323        });
  324        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  325        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  326        assert_eq!(
  327            editor.marked_text_ranges(cx),
  328            Some(vec![
  329                OffsetUtf16(0)..OffsetUtf16(3),
  330                OffsetUtf16(4)..OffsetUtf16(7),
  331                OffsetUtf16(8)..OffsetUtf16(11)
  332            ])
  333        );
  334
  335        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  336        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  337        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  338        assert_eq!(
  339            editor.marked_text_ranges(cx),
  340            Some(vec![
  341                OffsetUtf16(1)..OffsetUtf16(2),
  342                OffsetUtf16(5)..OffsetUtf16(6),
  343                OffsetUtf16(9)..OffsetUtf16(10)
  344            ])
  345        );
  346
  347        // Finalize IME composition with multiple cursors.
  348        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  349        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  350        assert_eq!(editor.marked_text_ranges(cx), None);
  351
  352        editor
  353    });
  354}
  355
  356#[gpui::test]
  357fn test_selection_with_mouse(cx: &mut TestAppContext) {
  358    init_test(cx, |_| {});
  359
  360    let editor = cx.add_window(|window, cx| {
  361        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  362        build_editor(buffer, window, cx)
  363    });
  364
  365    _ = editor.update(cx, |editor, window, cx| {
  366        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  367    });
  368    assert_eq!(
  369        editor
  370            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  371            .unwrap(),
  372        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  373    );
  374
  375    _ = editor.update(cx, |editor, window, cx| {
  376        editor.update_selection(
  377            DisplayPoint::new(DisplayRow(3), 3),
  378            0,
  379            gpui::Point::<f32>::default(),
  380            window,
  381            cx,
  382        );
  383    });
  384
  385    assert_eq!(
  386        editor
  387            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  388            .unwrap(),
  389        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  390    );
  391
  392    _ = editor.update(cx, |editor, window, cx| {
  393        editor.update_selection(
  394            DisplayPoint::new(DisplayRow(1), 1),
  395            0,
  396            gpui::Point::<f32>::default(),
  397            window,
  398            cx,
  399        );
  400    });
  401
  402    assert_eq!(
  403        editor
  404            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  405            .unwrap(),
  406        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  407    );
  408
  409    _ = editor.update(cx, |editor, window, cx| {
  410        editor.end_selection(window, cx);
  411        editor.update_selection(
  412            DisplayPoint::new(DisplayRow(3), 3),
  413            0,
  414            gpui::Point::<f32>::default(),
  415            window,
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  423            .unwrap(),
  424        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  425    );
  426
  427    _ = editor.update(cx, |editor, window, cx| {
  428        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  429        editor.update_selection(
  430            DisplayPoint::new(DisplayRow(0), 0),
  431            0,
  432            gpui::Point::<f32>::default(),
  433            window,
  434            cx,
  435        );
  436    });
  437
  438    assert_eq!(
  439        editor
  440            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  441            .unwrap(),
  442        [
  443            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  444            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  445        ]
  446    );
  447
  448    _ = editor.update(cx, |editor, window, cx| {
  449        editor.end_selection(window, cx);
  450    });
  451
  452    assert_eq!(
  453        editor
  454            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  455            .unwrap(),
  456        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  457    );
  458}
  459
  460#[gpui::test]
  461fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  462    init_test(cx, |_| {});
  463
  464    let editor = cx.add_window(|window, cx| {
  465        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  466        build_editor(buffer, window, cx)
  467    });
  468
  469    _ = editor.update(cx, |editor, window, cx| {
  470        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  471    });
  472
  473    _ = editor.update(cx, |editor, window, cx| {
  474        editor.end_selection(window, cx);
  475    });
  476
  477    _ = editor.update(cx, |editor, window, cx| {
  478        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  479    });
  480
  481    _ = editor.update(cx, |editor, window, cx| {
  482        editor.end_selection(window, cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  488            .unwrap(),
  489        [
  490            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  491            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  492        ]
  493    );
  494
  495    _ = editor.update(cx, |editor, window, cx| {
  496        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  497    });
  498
  499    _ = editor.update(cx, |editor, window, cx| {
  500        editor.end_selection(window, cx);
  501    });
  502
  503    assert_eq!(
  504        editor
  505            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  506            .unwrap(),
  507        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  508    );
  509}
  510
  511#[gpui::test]
  512fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  513    init_test(cx, |_| {});
  514
  515    let editor = cx.add_window(|window, cx| {
  516        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  517        build_editor(buffer, window, cx)
  518    });
  519
  520    _ = editor.update(cx, |editor, window, cx| {
  521        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  522        assert_eq!(
  523            editor.selections.display_ranges(cx),
  524            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  525        );
  526    });
  527
  528    _ = editor.update(cx, |editor, window, cx| {
  529        editor.update_selection(
  530            DisplayPoint::new(DisplayRow(3), 3),
  531            0,
  532            gpui::Point::<f32>::default(),
  533            window,
  534            cx,
  535        );
  536        assert_eq!(
  537            editor.selections.display_ranges(cx),
  538            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  539        );
  540    });
  541
  542    _ = editor.update(cx, |editor, window, cx| {
  543        editor.cancel(&Cancel, window, cx);
  544        editor.update_selection(
  545            DisplayPoint::new(DisplayRow(1), 1),
  546            0,
  547            gpui::Point::<f32>::default(),
  548            window,
  549            cx,
  550        );
  551        assert_eq!(
  552            editor.selections.display_ranges(cx),
  553            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  554        );
  555    });
  556}
  557
  558#[gpui::test]
  559fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  560    init_test(cx, |_| {});
  561
  562    let editor = cx.add_window(|window, cx| {
  563        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  564        build_editor(buffer, window, cx)
  565    });
  566
  567    _ = editor.update(cx, |editor, window, cx| {
  568        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  569        assert_eq!(
  570            editor.selections.display_ranges(cx),
  571            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  572        );
  573
  574        editor.move_down(&Default::default(), window, cx);
  575        assert_eq!(
  576            editor.selections.display_ranges(cx),
  577            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  578        );
  579
  580        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  581        assert_eq!(
  582            editor.selections.display_ranges(cx),
  583            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  584        );
  585
  586        editor.move_up(&Default::default(), window, cx);
  587        assert_eq!(
  588            editor.selections.display_ranges(cx),
  589            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  590        );
  591    });
  592}
  593
  594#[gpui::test]
  595fn test_clone(cx: &mut TestAppContext) {
  596    init_test(cx, |_| {});
  597
  598    let (text, selection_ranges) = marked_text_ranges(
  599        indoc! {"
  600            one
  601            two
  602            threeˇ
  603            four
  604            fiveˇ
  605        "},
  606        true,
  607    );
  608
  609    let editor = cx.add_window(|window, cx| {
  610        let buffer = MultiBuffer::build_simple(&text, cx);
  611        build_editor(buffer, window, cx)
  612    });
  613
  614    _ = editor.update(cx, |editor, window, cx| {
  615        editor.change_selections(None, window, cx, |s| {
  616            s.select_ranges(selection_ranges.clone())
  617        });
  618        editor.fold_creases(
  619            vec![
  620                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  621                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  622            ],
  623            true,
  624            window,
  625            cx,
  626        );
  627    });
  628
  629    let cloned_editor = editor
  630        .update(cx, |editor, _, cx| {
  631            cx.open_window(Default::default(), |window, cx| {
  632                cx.new(|cx| editor.clone(window, cx))
  633            })
  634        })
  635        .unwrap()
  636        .unwrap();
  637
  638    let snapshot = editor
  639        .update(cx, |e, window, cx| e.snapshot(window, cx))
  640        .unwrap();
  641    let cloned_snapshot = cloned_editor
  642        .update(cx, |e, window, cx| e.snapshot(window, cx))
  643        .unwrap();
  644
  645    assert_eq!(
  646        cloned_editor
  647            .update(cx, |e, _, cx| e.display_text(cx))
  648            .unwrap(),
  649        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  650    );
  651    assert_eq!(
  652        cloned_snapshot
  653            .folds_in_range(0..text.len())
  654            .collect::<Vec<_>>(),
  655        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  656    );
  657    assert_set_eq!(
  658        cloned_editor
  659            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  660            .unwrap(),
  661        editor
  662            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  663            .unwrap()
  664    );
  665    assert_set_eq!(
  666        cloned_editor
  667            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  668            .unwrap(),
  669        editor
  670            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  671            .unwrap()
  672    );
  673}
  674
  675#[gpui::test]
  676async fn test_navigation_history(cx: &mut TestAppContext) {
  677    init_test(cx, |_| {});
  678
  679    use workspace::item::Item;
  680
  681    let fs = FakeFs::new(cx.executor());
  682    let project = Project::test(fs, [], cx).await;
  683    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  684    let pane = workspace
  685        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  686        .unwrap();
  687
  688    _ = workspace.update(cx, |_v, window, cx| {
  689        cx.new(|cx| {
  690            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  691            let mut editor = build_editor(buffer.clone(), window, cx);
  692            let handle = cx.entity();
  693            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  694
  695            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  696                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  697            }
  698
  699            // Move the cursor a small distance.
  700            // Nothing is added to the navigation history.
  701            editor.change_selections(None, window, cx, |s| {
  702                s.select_display_ranges([
  703                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  704                ])
  705            });
  706            editor.change_selections(None, window, cx, |s| {
  707                s.select_display_ranges([
  708                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  709                ])
  710            });
  711            assert!(pop_history(&mut editor, cx).is_none());
  712
  713            // Move the cursor a large distance.
  714            // The history can jump back to the previous position.
  715            editor.change_selections(None, window, cx, |s| {
  716                s.select_display_ranges([
  717                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  718                ])
  719            });
  720            let nav_entry = pop_history(&mut editor, cx).unwrap();
  721            editor.navigate(nav_entry.data.unwrap(), window, cx);
  722            assert_eq!(nav_entry.item.id(), cx.entity_id());
  723            assert_eq!(
  724                editor.selections.display_ranges(cx),
  725                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  726            );
  727            assert!(pop_history(&mut editor, cx).is_none());
  728
  729            // Move the cursor a small distance via the mouse.
  730            // Nothing is added to the navigation history.
  731            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  732            editor.end_selection(window, cx);
  733            assert_eq!(
  734                editor.selections.display_ranges(cx),
  735                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  736            );
  737            assert!(pop_history(&mut editor, cx).is_none());
  738
  739            // Move the cursor a large distance via the mouse.
  740            // The history can jump back to the previous position.
  741            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  742            editor.end_selection(window, cx);
  743            assert_eq!(
  744                editor.selections.display_ranges(cx),
  745                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  746            );
  747            let nav_entry = pop_history(&mut editor, cx).unwrap();
  748            editor.navigate(nav_entry.data.unwrap(), window, cx);
  749            assert_eq!(nav_entry.item.id(), cx.entity_id());
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  753            );
  754            assert!(pop_history(&mut editor, cx).is_none());
  755
  756            // Set scroll position to check later
  757            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  758            let original_scroll_position = editor.scroll_manager.anchor();
  759
  760            // Jump to the end of the document and adjust scroll
  761            editor.move_to_end(&MoveToEnd, window, cx);
  762            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  763            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  764
  765            let nav_entry = pop_history(&mut editor, cx).unwrap();
  766            editor.navigate(nav_entry.data.unwrap(), window, cx);
  767            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  768
  769            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  770            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  771            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  772            let invalid_point = Point::new(9999, 0);
  773            editor.navigate(
  774                Box::new(NavigationData {
  775                    cursor_anchor: invalid_anchor,
  776                    cursor_position: invalid_point,
  777                    scroll_anchor: ScrollAnchor {
  778                        anchor: invalid_anchor,
  779                        offset: Default::default(),
  780                    },
  781                    scroll_top_row: invalid_point.row,
  782                }),
  783                window,
  784                cx,
  785            );
  786            assert_eq!(
  787                editor.selections.display_ranges(cx),
  788                &[editor.max_point(cx)..editor.max_point(cx)]
  789            );
  790            assert_eq!(
  791                editor.scroll_position(cx),
  792                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  793            );
  794
  795            editor
  796        })
  797    });
  798}
  799
  800#[gpui::test]
  801fn test_cancel(cx: &mut TestAppContext) {
  802    init_test(cx, |_| {});
  803
  804    let editor = cx.add_window(|window, cx| {
  805        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  806        build_editor(buffer, window, cx)
  807    });
  808
  809    _ = editor.update(cx, |editor, window, cx| {
  810        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  811        editor.update_selection(
  812            DisplayPoint::new(DisplayRow(1), 1),
  813            0,
  814            gpui::Point::<f32>::default(),
  815            window,
  816            cx,
  817        );
  818        editor.end_selection(window, cx);
  819
  820        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  821        editor.update_selection(
  822            DisplayPoint::new(DisplayRow(0), 3),
  823            0,
  824            gpui::Point::<f32>::default(),
  825            window,
  826            cx,
  827        );
  828        editor.end_selection(window, cx);
  829        assert_eq!(
  830            editor.selections.display_ranges(cx),
  831            [
  832                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  833                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  834            ]
  835        );
  836    });
  837
  838    _ = editor.update(cx, |editor, window, cx| {
  839        editor.cancel(&Cancel, window, cx);
  840        assert_eq!(
  841            editor.selections.display_ranges(cx),
  842            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  843        );
  844    });
  845
  846    _ = editor.update(cx, |editor, window, cx| {
  847        editor.cancel(&Cancel, window, cx);
  848        assert_eq!(
  849            editor.selections.display_ranges(cx),
  850            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  851        );
  852    });
  853}
  854
  855#[gpui::test]
  856fn test_fold_action(cx: &mut TestAppContext) {
  857    init_test(cx, |_| {});
  858
  859    let editor = cx.add_window(|window, cx| {
  860        let buffer = MultiBuffer::build_simple(
  861            &"
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {
  870                        2
  871                    }
  872
  873                    fn c() {
  874                        3
  875                    }
  876                }
  877            "
  878            .unindent(),
  879            cx,
  880        );
  881        build_editor(buffer.clone(), window, cx)
  882    });
  883
  884    _ = editor.update(cx, |editor, window, cx| {
  885        editor.change_selections(None, window, cx, |s| {
  886            s.select_display_ranges([
  887                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  888            ]);
  889        });
  890        editor.fold(&Fold, window, cx);
  891        assert_eq!(
  892            editor.display_text(cx),
  893            "
  894                impl Foo {
  895                    // Hello!
  896
  897                    fn a() {
  898                        1
  899                    }
  900
  901                    fn b() {⋯
  902                    }
  903
  904                    fn c() {⋯
  905                    }
  906                }
  907            "
  908            .unindent(),
  909        );
  910
  911        editor.fold(&Fold, window, cx);
  912        assert_eq!(
  913            editor.display_text(cx),
  914            "
  915                impl Foo {⋯
  916                }
  917            "
  918            .unindent(),
  919        );
  920
  921        editor.unfold_lines(&UnfoldLines, window, cx);
  922        assert_eq!(
  923            editor.display_text(cx),
  924            "
  925                impl Foo {
  926                    // Hello!
  927
  928                    fn a() {
  929                        1
  930                    }
  931
  932                    fn b() {⋯
  933                    }
  934
  935                    fn c() {⋯
  936                    }
  937                }
  938            "
  939            .unindent(),
  940        );
  941
  942        editor.unfold_lines(&UnfoldLines, window, cx);
  943        assert_eq!(
  944            editor.display_text(cx),
  945            editor.buffer.read(cx).read(cx).text()
  946        );
  947    });
  948}
  949
  950#[gpui::test]
  951fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  952    init_test(cx, |_| {});
  953
  954    let editor = cx.add_window(|window, cx| {
  955        let buffer = MultiBuffer::build_simple(
  956            &"
  957                class Foo:
  958                    # Hello!
  959
  960                    def a():
  961                        print(1)
  962
  963                    def b():
  964                        print(2)
  965
  966                    def c():
  967                        print(3)
  968            "
  969            .unindent(),
  970            cx,
  971        );
  972        build_editor(buffer.clone(), window, cx)
  973    });
  974
  975    _ = editor.update(cx, |editor, window, cx| {
  976        editor.change_selections(None, window, cx, |s| {
  977            s.select_display_ranges([
  978                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  979            ]);
  980        });
  981        editor.fold(&Fold, window, cx);
  982        assert_eq!(
  983            editor.display_text(cx),
  984            "
  985                class Foo:
  986                    # Hello!
  987
  988                    def a():
  989                        print(1)
  990
  991                    def b():⋯
  992
  993                    def c():⋯
  994            "
  995            .unindent(),
  996        );
  997
  998        editor.fold(&Fold, window, cx);
  999        assert_eq!(
 1000            editor.display_text(cx),
 1001            "
 1002                class Foo:⋯
 1003            "
 1004            .unindent(),
 1005        );
 1006
 1007        editor.unfold_lines(&UnfoldLines, window, cx);
 1008        assert_eq!(
 1009            editor.display_text(cx),
 1010            "
 1011                class Foo:
 1012                    # Hello!
 1013
 1014                    def a():
 1015                        print(1)
 1016
 1017                    def b():⋯
 1018
 1019                    def c():⋯
 1020            "
 1021            .unindent(),
 1022        );
 1023
 1024        editor.unfold_lines(&UnfoldLines, window, cx);
 1025        assert_eq!(
 1026            editor.display_text(cx),
 1027            editor.buffer.read(cx).read(cx).text()
 1028        );
 1029    });
 1030}
 1031
 1032#[gpui::test]
 1033fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1034    init_test(cx, |_| {});
 1035
 1036    let editor = cx.add_window(|window, cx| {
 1037        let buffer = MultiBuffer::build_simple(
 1038            &"
 1039                class Foo:
 1040                    # Hello!
 1041
 1042                    def a():
 1043                        print(1)
 1044
 1045                    def b():
 1046                        print(2)
 1047
 1048
 1049                    def c():
 1050                        print(3)
 1051
 1052
 1053            "
 1054            .unindent(),
 1055            cx,
 1056        );
 1057        build_editor(buffer.clone(), window, cx)
 1058    });
 1059
 1060    _ = editor.update(cx, |editor, window, cx| {
 1061        editor.change_selections(None, window, cx, |s| {
 1062            s.select_display_ranges([
 1063                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1064            ]);
 1065        });
 1066        editor.fold(&Fold, window, cx);
 1067        assert_eq!(
 1068            editor.display_text(cx),
 1069            "
 1070                class Foo:
 1071                    # Hello!
 1072
 1073                    def a():
 1074                        print(1)
 1075
 1076                    def b():⋯
 1077
 1078
 1079                    def c():⋯
 1080
 1081
 1082            "
 1083            .unindent(),
 1084        );
 1085
 1086        editor.fold(&Fold, window, cx);
 1087        assert_eq!(
 1088            editor.display_text(cx),
 1089            "
 1090                class Foo:⋯
 1091
 1092
 1093            "
 1094            .unindent(),
 1095        );
 1096
 1097        editor.unfold_lines(&UnfoldLines, window, cx);
 1098        assert_eq!(
 1099            editor.display_text(cx),
 1100            "
 1101                class Foo:
 1102                    # Hello!
 1103
 1104                    def a():
 1105                        print(1)
 1106
 1107                    def b():⋯
 1108
 1109
 1110                    def c():⋯
 1111
 1112
 1113            "
 1114            .unindent(),
 1115        );
 1116
 1117        editor.unfold_lines(&UnfoldLines, window, cx);
 1118        assert_eq!(
 1119            editor.display_text(cx),
 1120            editor.buffer.read(cx).read(cx).text()
 1121        );
 1122    });
 1123}
 1124
 1125#[gpui::test]
 1126fn test_fold_at_level(cx: &mut TestAppContext) {
 1127    init_test(cx, |_| {});
 1128
 1129    let editor = cx.add_window(|window, cx| {
 1130        let buffer = MultiBuffer::build_simple(
 1131            &"
 1132                class Foo:
 1133                    # Hello!
 1134
 1135                    def a():
 1136                        print(1)
 1137
 1138                    def b():
 1139                        print(2)
 1140
 1141
 1142                class Bar:
 1143                    # World!
 1144
 1145                    def a():
 1146                        print(1)
 1147
 1148                    def b():
 1149                        print(2)
 1150
 1151
 1152            "
 1153            .unindent(),
 1154            cx,
 1155        );
 1156        build_editor(buffer.clone(), window, cx)
 1157    });
 1158
 1159    _ = editor.update(cx, |editor, window, cx| {
 1160        editor.fold_at_level(&FoldAtLevel { level: 2 }, window, cx);
 1161        assert_eq!(
 1162            editor.display_text(cx),
 1163            "
 1164                class Foo:
 1165                    # Hello!
 1166
 1167                    def a():⋯
 1168
 1169                    def b():⋯
 1170
 1171
 1172                class Bar:
 1173                    # World!
 1174
 1175                    def a():⋯
 1176
 1177                    def b():⋯
 1178
 1179
 1180            "
 1181            .unindent(),
 1182        );
 1183
 1184        editor.fold_at_level(&FoldAtLevel { level: 1 }, window, cx);
 1185        assert_eq!(
 1186            editor.display_text(cx),
 1187            "
 1188                class Foo:⋯
 1189
 1190
 1191                class Bar:⋯
 1192
 1193
 1194            "
 1195            .unindent(),
 1196        );
 1197
 1198        editor.unfold_all(&UnfoldAll, window, cx);
 1199        editor.fold_at_level(&FoldAtLevel { level: 0 }, window, cx);
 1200        assert_eq!(
 1201            editor.display_text(cx),
 1202            "
 1203                class Foo:
 1204                    # Hello!
 1205
 1206                    def a():
 1207                        print(1)
 1208
 1209                    def b():
 1210                        print(2)
 1211
 1212
 1213                class Bar:
 1214                    # World!
 1215
 1216                    def a():
 1217                        print(1)
 1218
 1219                    def b():
 1220                        print(2)
 1221
 1222
 1223            "
 1224            .unindent(),
 1225        );
 1226
 1227        assert_eq!(
 1228            editor.display_text(cx),
 1229            editor.buffer.read(cx).read(cx).text()
 1230        );
 1231    });
 1232}
 1233
 1234#[gpui::test]
 1235fn test_move_cursor(cx: &mut TestAppContext) {
 1236    init_test(cx, |_| {});
 1237
 1238    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1239    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1240
 1241    buffer.update(cx, |buffer, cx| {
 1242        buffer.edit(
 1243            vec![
 1244                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1245                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1246            ],
 1247            None,
 1248            cx,
 1249        );
 1250    });
 1251    _ = editor.update(cx, |editor, window, cx| {
 1252        assert_eq!(
 1253            editor.selections.display_ranges(cx),
 1254            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1255        );
 1256
 1257        editor.move_down(&MoveDown, window, cx);
 1258        assert_eq!(
 1259            editor.selections.display_ranges(cx),
 1260            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1261        );
 1262
 1263        editor.move_right(&MoveRight, window, cx);
 1264        assert_eq!(
 1265            editor.selections.display_ranges(cx),
 1266            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1267        );
 1268
 1269        editor.move_left(&MoveLeft, window, cx);
 1270        assert_eq!(
 1271            editor.selections.display_ranges(cx),
 1272            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1273        );
 1274
 1275        editor.move_up(&MoveUp, window, cx);
 1276        assert_eq!(
 1277            editor.selections.display_ranges(cx),
 1278            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1279        );
 1280
 1281        editor.move_to_end(&MoveToEnd, window, cx);
 1282        assert_eq!(
 1283            editor.selections.display_ranges(cx),
 1284            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1285        );
 1286
 1287        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1288        assert_eq!(
 1289            editor.selections.display_ranges(cx),
 1290            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1291        );
 1292
 1293        editor.change_selections(None, window, cx, |s| {
 1294            s.select_display_ranges([
 1295                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1296            ]);
 1297        });
 1298        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1299        assert_eq!(
 1300            editor.selections.display_ranges(cx),
 1301            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1302        );
 1303
 1304        editor.select_to_end(&SelectToEnd, window, cx);
 1305        assert_eq!(
 1306            editor.selections.display_ranges(cx),
 1307            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1308        );
 1309    });
 1310}
 1311
 1312// TODO: Re-enable this test
 1313#[cfg(target_os = "macos")]
 1314#[gpui::test]
 1315fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1316    init_test(cx, |_| {});
 1317
 1318    let editor = cx.add_window(|window, cx| {
 1319        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1320        build_editor(buffer.clone(), window, cx)
 1321    });
 1322
 1323    assert_eq!('🟥'.len_utf8(), 4);
 1324    assert_eq!('α'.len_utf8(), 2);
 1325
 1326    _ = editor.update(cx, |editor, window, cx| {
 1327        editor.fold_creases(
 1328            vec![
 1329                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1330                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1331                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1332            ],
 1333            true,
 1334            window,
 1335            cx,
 1336        );
 1337        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1338
 1339        editor.move_right(&MoveRight, window, cx);
 1340        assert_eq!(
 1341            editor.selections.display_ranges(cx),
 1342            &[empty_range(0, "🟥".len())]
 1343        );
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥🟧".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧⋯".len())]
 1353        );
 1354
 1355        editor.move_down(&MoveDown, window, cx);
 1356        assert_eq!(
 1357            editor.selections.display_ranges(cx),
 1358            &[empty_range(1, "ab⋯e".len())]
 1359        );
 1360        editor.move_left(&MoveLeft, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "a".len())]
 1374        );
 1375
 1376        editor.move_down(&MoveDown, window, cx);
 1377        assert_eq!(
 1378            editor.selections.display_ranges(cx),
 1379            &[empty_range(2, "α".len())]
 1380        );
 1381        editor.move_right(&MoveRight, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "αβ".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ⋯".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯ε".len())]
 1395        );
 1396
 1397        editor.move_up(&MoveUp, window, cx);
 1398        assert_eq!(
 1399            editor.selections.display_ranges(cx),
 1400            &[empty_range(1, "ab⋯e".len())]
 1401        );
 1402        editor.move_down(&MoveDown, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(2, "αβ⋯ε".len())]
 1406        );
 1407        editor.move_up(&MoveUp, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(1, "ab⋯e".len())]
 1411        );
 1412
 1413        editor.move_up(&MoveUp, window, cx);
 1414        assert_eq!(
 1415            editor.selections.display_ranges(cx),
 1416            &[empty_range(0, "🟥🟧".len())]
 1417        );
 1418        editor.move_left(&MoveLeft, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "".len())]
 1427        );
 1428    });
 1429}
 1430
 1431#[gpui::test]
 1432fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1433    init_test(cx, |_| {});
 1434
 1435    let editor = cx.add_window(|window, cx| {
 1436        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1437        build_editor(buffer.clone(), window, cx)
 1438    });
 1439    _ = editor.update(cx, |editor, window, cx| {
 1440        editor.change_selections(None, window, cx, |s| {
 1441            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1442        });
 1443
 1444        // moving above start of document should move selection to start of document,
 1445        // but the next move down should still be at the original goal_x
 1446        editor.move_up(&MoveUp, window, cx);
 1447        assert_eq!(
 1448            editor.selections.display_ranges(cx),
 1449            &[empty_range(0, "".len())]
 1450        );
 1451
 1452        editor.move_down(&MoveDown, window, cx);
 1453        assert_eq!(
 1454            editor.selections.display_ranges(cx),
 1455            &[empty_range(1, "abcd".len())]
 1456        );
 1457
 1458        editor.move_down(&MoveDown, window, cx);
 1459        assert_eq!(
 1460            editor.selections.display_ranges(cx),
 1461            &[empty_range(2, "αβγ".len())]
 1462        );
 1463
 1464        editor.move_down(&MoveDown, window, cx);
 1465        assert_eq!(
 1466            editor.selections.display_ranges(cx),
 1467            &[empty_range(3, "abcd".len())]
 1468        );
 1469
 1470        editor.move_down(&MoveDown, window, cx);
 1471        assert_eq!(
 1472            editor.selections.display_ranges(cx),
 1473            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1474        );
 1475
 1476        // moving past end of document should not change goal_x
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(5, "".len())]
 1481        );
 1482
 1483        editor.move_down(&MoveDown, window, cx);
 1484        assert_eq!(
 1485            editor.selections.display_ranges(cx),
 1486            &[empty_range(5, "".len())]
 1487        );
 1488
 1489        editor.move_up(&MoveUp, window, cx);
 1490        assert_eq!(
 1491            editor.selections.display_ranges(cx),
 1492            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1493        );
 1494
 1495        editor.move_up(&MoveUp, window, cx);
 1496        assert_eq!(
 1497            editor.selections.display_ranges(cx),
 1498            &[empty_range(3, "abcd".len())]
 1499        );
 1500
 1501        editor.move_up(&MoveUp, window, cx);
 1502        assert_eq!(
 1503            editor.selections.display_ranges(cx),
 1504            &[empty_range(2, "αβγ".len())]
 1505        );
 1506    });
 1507}
 1508
 1509#[gpui::test]
 1510fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1511    init_test(cx, |_| {});
 1512    let move_to_beg = MoveToBeginningOfLine {
 1513        stop_at_soft_wraps: true,
 1514    };
 1515
 1516    let move_to_end = MoveToEndOfLine {
 1517        stop_at_soft_wraps: true,
 1518    };
 1519
 1520    let editor = cx.add_window(|window, cx| {
 1521        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1522        build_editor(buffer, window, cx)
 1523    });
 1524    _ = editor.update(cx, |editor, window, cx| {
 1525        editor.change_selections(None, window, cx, |s| {
 1526            s.select_display_ranges([
 1527                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1528                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1529            ]);
 1530        });
 1531    });
 1532
 1533    _ = editor.update(cx, |editor, window, cx| {
 1534        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1535        assert_eq!(
 1536            editor.selections.display_ranges(cx),
 1537            &[
 1538                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1539                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1540            ]
 1541        );
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_end_of_line(&move_to_end, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1572                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1573            ]
 1574        );
 1575    });
 1576
 1577    // Moving to the end of line again is a no-op.
 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    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_left(&MoveLeft, window, cx);
 1591        editor.select_to_beginning_of_line(
 1592            &SelectToBeginningOfLine {
 1593                stop_at_soft_wraps: true,
 1594            },
 1595            window,
 1596            cx,
 1597        );
 1598        assert_eq!(
 1599            editor.selections.display_ranges(cx),
 1600            &[
 1601                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1602                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1603            ]
 1604        );
 1605    });
 1606
 1607    _ = editor.update(cx, |editor, window, cx| {
 1608        editor.select_to_beginning_of_line(
 1609            &SelectToBeginningOfLine {
 1610                stop_at_soft_wraps: true,
 1611            },
 1612            window,
 1613            cx,
 1614        );
 1615        assert_eq!(
 1616            editor.selections.display_ranges(cx),
 1617            &[
 1618                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1619                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1620            ]
 1621        );
 1622    });
 1623
 1624    _ = editor.update(cx, |editor, window, cx| {
 1625        editor.select_to_beginning_of_line(
 1626            &SelectToBeginningOfLine {
 1627                stop_at_soft_wraps: true,
 1628            },
 1629            window,
 1630            cx,
 1631        );
 1632        assert_eq!(
 1633            editor.selections.display_ranges(cx),
 1634            &[
 1635                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1636                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1637            ]
 1638        );
 1639    });
 1640
 1641    _ = editor.update(cx, |editor, window, cx| {
 1642        editor.select_to_end_of_line(
 1643            &SelectToEndOfLine {
 1644                stop_at_soft_wraps: true,
 1645            },
 1646            window,
 1647            cx,
 1648        );
 1649        assert_eq!(
 1650            editor.selections.display_ranges(cx),
 1651            &[
 1652                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1653                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1654            ]
 1655        );
 1656    });
 1657
 1658    _ = editor.update(cx, |editor, window, cx| {
 1659        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1660        assert_eq!(editor.display_text(cx), "ab\n  de");
 1661        assert_eq!(
 1662            editor.selections.display_ranges(cx),
 1663            &[
 1664                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1665                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1666            ]
 1667        );
 1668    });
 1669
 1670    _ = editor.update(cx, |editor, window, cx| {
 1671        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1672        assert_eq!(editor.display_text(cx), "\n");
 1673        assert_eq!(
 1674            editor.selections.display_ranges(cx),
 1675            &[
 1676                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1677                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1678            ]
 1679        );
 1680    });
 1681}
 1682
 1683#[gpui::test]
 1684fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1685    init_test(cx, |_| {});
 1686    let move_to_beg = MoveToBeginningOfLine {
 1687        stop_at_soft_wraps: false,
 1688    };
 1689
 1690    let move_to_end = MoveToEndOfLine {
 1691        stop_at_soft_wraps: false,
 1692    };
 1693
 1694    let editor = cx.add_window(|window, cx| {
 1695        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1696        build_editor(buffer, window, cx)
 1697    });
 1698
 1699    _ = editor.update(cx, |editor, window, cx| {
 1700        editor.set_wrap_width(Some(140.0.into()), cx);
 1701
 1702        // We expect the following lines after wrapping
 1703        // ```
 1704        // thequickbrownfox
 1705        // jumpedoverthelazydo
 1706        // gs
 1707        // ```
 1708        // The final `gs` was soft-wrapped onto a new line.
 1709        assert_eq!(
 1710            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1711            editor.display_text(cx),
 1712        );
 1713
 1714        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1715        // Start the cursor at the `k` on the first line
 1716        editor.change_selections(None, window, cx, |s| {
 1717            s.select_display_ranges([
 1718                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1719            ]);
 1720        });
 1721
 1722        // Moving to the beginning of the line should put us at the beginning of the line.
 1723        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1724        assert_eq!(
 1725            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1726            editor.selections.display_ranges(cx)
 1727        );
 1728
 1729        // Moving to the end of the line should put us at the end of the line.
 1730        editor.move_to_end_of_line(&move_to_end, window, cx);
 1731        assert_eq!(
 1732            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1733            editor.selections.display_ranges(cx)
 1734        );
 1735
 1736        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1737        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1738        editor.change_selections(None, window, cx, |s| {
 1739            s.select_display_ranges([
 1740                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1741            ]);
 1742        });
 1743
 1744        // Moving to the beginning of the line should put us at the start of the second line of
 1745        // display text, i.e., the `j`.
 1746        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Moving to the beginning of the line again should be a no-op.
 1753        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1754        assert_eq!(
 1755            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1756            editor.selections.display_ranges(cx)
 1757        );
 1758
 1759        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1760        // next display line.
 1761        editor.move_to_end_of_line(&move_to_end, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the end of the line again should be a no-op.
 1768        editor.move_to_end_of_line(&move_to_end, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773    });
 1774}
 1775
 1776#[gpui::test]
 1777fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1778    init_test(cx, |_| {});
 1779
 1780    let editor = cx.add_window(|window, cx| {
 1781        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1782        build_editor(buffer, window, cx)
 1783    });
 1784    _ = editor.update(cx, |editor, window, cx| {
 1785        editor.change_selections(None, window, cx, |s| {
 1786            s.select_display_ranges([
 1787                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1788                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1789            ])
 1790        });
 1791
 1792        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1793        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1794
 1795        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1796        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1797
 1798        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1799        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1800
 1801        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1802        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1803
 1804        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1805        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1806
 1807        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1808        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1809
 1810        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1811        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1812
 1813        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1814        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1815
 1816        editor.move_right(&MoveRight, window, cx);
 1817        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1818        assert_selection_ranges(
 1819            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1820            editor,
 1821            cx,
 1822        );
 1823
 1824        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1825        assert_selection_ranges(
 1826            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1827            editor,
 1828            cx,
 1829        );
 1830
 1831        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1832        assert_selection_ranges(
 1833            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1834            editor,
 1835            cx,
 1836        );
 1837    });
 1838}
 1839
 1840#[gpui::test]
 1841fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1842    init_test(cx, |_| {});
 1843
 1844    let editor = cx.add_window(|window, cx| {
 1845        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1846        build_editor(buffer, window, cx)
 1847    });
 1848
 1849    _ = editor.update(cx, |editor, window, cx| {
 1850        editor.set_wrap_width(Some(140.0.into()), cx);
 1851        assert_eq!(
 1852            editor.display_text(cx),
 1853            "use one::{\n    two::three::\n    four::five\n};"
 1854        );
 1855
 1856        editor.change_selections(None, window, cx, |s| {
 1857            s.select_display_ranges([
 1858                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1859            ]);
 1860        });
 1861
 1862        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1866        );
 1867
 1868        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1869        assert_eq!(
 1870            editor.selections.display_ranges(cx),
 1871            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1872        );
 1873
 1874        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1878        );
 1879
 1880        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1881        assert_eq!(
 1882            editor.selections.display_ranges(cx),
 1883            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1884        );
 1885
 1886        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1887        assert_eq!(
 1888            editor.selections.display_ranges(cx),
 1889            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1890        );
 1891
 1892        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1893        assert_eq!(
 1894            editor.selections.display_ranges(cx),
 1895            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1896        );
 1897    });
 1898}
 1899
 1900#[gpui::test]
 1901async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1902    init_test(cx, |_| {});
 1903    let mut cx = EditorTestContext::new(cx).await;
 1904
 1905    let line_height = cx.editor(|editor, window, _| {
 1906        editor
 1907            .style()
 1908            .unwrap()
 1909            .text
 1910            .line_height_in_pixels(window.rem_size())
 1911    });
 1912    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1913
 1914    cx.set_state(
 1915        &r#"ˇone
 1916        two
 1917
 1918        three
 1919        fourˇ
 1920        five
 1921
 1922        six"#
 1923            .unindent(),
 1924    );
 1925
 1926    cx.update_editor(|editor, window, cx| {
 1927        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1928    });
 1929    cx.assert_editor_state(
 1930        &r#"one
 1931        two
 1932        ˇ
 1933        three
 1934        four
 1935        five
 1936        ˇ
 1937        six"#
 1938            .unindent(),
 1939    );
 1940
 1941    cx.update_editor(|editor, window, cx| {
 1942        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1943    });
 1944    cx.assert_editor_state(
 1945        &r#"one
 1946        two
 1947
 1948        three
 1949        four
 1950        five
 1951        ˇ
 1952        sixˇ"#
 1953            .unindent(),
 1954    );
 1955
 1956    cx.update_editor(|editor, window, cx| {
 1957        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1958    });
 1959    cx.assert_editor_state(
 1960        &r#"one
 1961        two
 1962
 1963        three
 1964        four
 1965        five
 1966
 1967        sixˇ"#
 1968            .unindent(),
 1969    );
 1970
 1971    cx.update_editor(|editor, window, cx| {
 1972        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1973    });
 1974    cx.assert_editor_state(
 1975        &r#"one
 1976        two
 1977
 1978        three
 1979        four
 1980        five
 1981        ˇ
 1982        six"#
 1983            .unindent(),
 1984    );
 1985
 1986    cx.update_editor(|editor, window, cx| {
 1987        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1988    });
 1989    cx.assert_editor_state(
 1990        &r#"one
 1991        two
 1992        ˇ
 1993        three
 1994        four
 1995        five
 1996
 1997        six"#
 1998            .unindent(),
 1999    );
 2000
 2001    cx.update_editor(|editor, window, cx| {
 2002        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2003    });
 2004    cx.assert_editor_state(
 2005        &r#"ˇone
 2006        two
 2007
 2008        three
 2009        four
 2010        five
 2011
 2012        six"#
 2013            .unindent(),
 2014    );
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    let window = cx.window;
 2029    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2030
 2031    cx.set_state(
 2032        r#"ˇone
 2033        two
 2034        three
 2035        four
 2036        five
 2037        six
 2038        seven
 2039        eight
 2040        nine
 2041        ten
 2042        "#,
 2043    );
 2044
 2045    cx.update_editor(|editor, window, cx| {
 2046        assert_eq!(
 2047            editor.snapshot(window, cx).scroll_position(),
 2048            gpui::Point::new(0., 0.)
 2049        );
 2050        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2051        assert_eq!(
 2052            editor.snapshot(window, cx).scroll_position(),
 2053            gpui::Point::new(0., 3.)
 2054        );
 2055        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2056        assert_eq!(
 2057            editor.snapshot(window, cx).scroll_position(),
 2058            gpui::Point::new(0., 6.)
 2059        );
 2060        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2061        assert_eq!(
 2062            editor.snapshot(window, cx).scroll_position(),
 2063            gpui::Point::new(0., 3.)
 2064        );
 2065
 2066        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2067        assert_eq!(
 2068            editor.snapshot(window, cx).scroll_position(),
 2069            gpui::Point::new(0., 1.)
 2070        );
 2071        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2072        assert_eq!(
 2073            editor.snapshot(window, cx).scroll_position(),
 2074            gpui::Point::new(0., 3.)
 2075        );
 2076    });
 2077}
 2078
 2079#[gpui::test]
 2080async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2081    init_test(cx, |_| {});
 2082    let mut cx = EditorTestContext::new(cx).await;
 2083
 2084    let line_height = cx.update_editor(|editor, window, cx| {
 2085        editor.set_vertical_scroll_margin(2, cx);
 2086        editor
 2087            .style()
 2088            .unwrap()
 2089            .text
 2090            .line_height_in_pixels(window.rem_size())
 2091    });
 2092    let window = cx.window;
 2093    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2094
 2095    cx.set_state(
 2096        r#"ˇone
 2097            two
 2098            three
 2099            four
 2100            five
 2101            six
 2102            seven
 2103            eight
 2104            nine
 2105            ten
 2106        "#,
 2107    );
 2108    cx.update_editor(|editor, window, cx| {
 2109        assert_eq!(
 2110            editor.snapshot(window, cx).scroll_position(),
 2111            gpui::Point::new(0., 0.0)
 2112        );
 2113    });
 2114
 2115    // Add a cursor below the visible area. Since both cursors cannot fit
 2116    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2117    // allows the vertical scroll margin below that cursor.
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2120            selections.select_ranges([
 2121                Point::new(0, 0)..Point::new(0, 0),
 2122                Point::new(6, 0)..Point::new(6, 0),
 2123            ]);
 2124        })
 2125    });
 2126    cx.update_editor(|editor, window, cx| {
 2127        assert_eq!(
 2128            editor.snapshot(window, cx).scroll_position(),
 2129            gpui::Point::new(0., 3.0)
 2130        );
 2131    });
 2132
 2133    // Move down. The editor cursor scrolls down to track the newest cursor.
 2134    cx.update_editor(|editor, window, cx| {
 2135        editor.move_down(&Default::default(), window, cx);
 2136    });
 2137    cx.update_editor(|editor, window, cx| {
 2138        assert_eq!(
 2139            editor.snapshot(window, cx).scroll_position(),
 2140            gpui::Point::new(0., 4.0)
 2141        );
 2142    });
 2143
 2144    // Add a cursor above the visible area. Since both cursors fit on screen,
 2145    // the editor scrolls to show both.
 2146    cx.update_editor(|editor, window, cx| {
 2147        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2148            selections.select_ranges([
 2149                Point::new(1, 0)..Point::new(1, 0),
 2150                Point::new(6, 0)..Point::new(6, 0),
 2151            ]);
 2152        })
 2153    });
 2154    cx.update_editor(|editor, window, cx| {
 2155        assert_eq!(
 2156            editor.snapshot(window, cx).scroll_position(),
 2157            gpui::Point::new(0., 1.0)
 2158        );
 2159    });
 2160}
 2161
 2162#[gpui::test]
 2163async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2164    init_test(cx, |_| {});
 2165    let mut cx = EditorTestContext::new(cx).await;
 2166
 2167    let line_height = cx.editor(|editor, window, _cx| {
 2168        editor
 2169            .style()
 2170            .unwrap()
 2171            .text
 2172            .line_height_in_pixels(window.rem_size())
 2173    });
 2174    let window = cx.window;
 2175    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2176    cx.set_state(
 2177        &r#"
 2178        ˇone
 2179        two
 2180        threeˇ
 2181        four
 2182        five
 2183        six
 2184        seven
 2185        eight
 2186        nine
 2187        ten
 2188        "#
 2189        .unindent(),
 2190    );
 2191
 2192    cx.update_editor(|editor, window, cx| {
 2193        editor.move_page_down(&MovePageDown::default(), window, cx)
 2194    });
 2195    cx.assert_editor_state(
 2196        &r#"
 2197        one
 2198        two
 2199        three
 2200        ˇfour
 2201        five
 2202        sixˇ
 2203        seven
 2204        eight
 2205        nine
 2206        ten
 2207        "#
 2208        .unindent(),
 2209    );
 2210
 2211    cx.update_editor(|editor, window, cx| {
 2212        editor.move_page_down(&MovePageDown::default(), window, cx)
 2213    });
 2214    cx.assert_editor_state(
 2215        &r#"
 2216        one
 2217        two
 2218        three
 2219        four
 2220        five
 2221        six
 2222        ˇseven
 2223        eight
 2224        nineˇ
 2225        ten
 2226        "#
 2227        .unindent(),
 2228    );
 2229
 2230    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2231    cx.assert_editor_state(
 2232        &r#"
 2233        one
 2234        two
 2235        three
 2236        ˇfour
 2237        five
 2238        sixˇ
 2239        seven
 2240        eight
 2241        nine
 2242        ten
 2243        "#
 2244        .unindent(),
 2245    );
 2246
 2247    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2248    cx.assert_editor_state(
 2249        &r#"
 2250        ˇone
 2251        two
 2252        threeˇ
 2253        four
 2254        five
 2255        six
 2256        seven
 2257        eight
 2258        nine
 2259        ten
 2260        "#
 2261        .unindent(),
 2262    );
 2263
 2264    // Test select collapsing
 2265    cx.update_editor(|editor, window, cx| {
 2266        editor.move_page_down(&MovePageDown::default(), window, cx);
 2267        editor.move_page_down(&MovePageDown::default(), window, cx);
 2268        editor.move_page_down(&MovePageDown::default(), window, cx);
 2269    });
 2270    cx.assert_editor_state(
 2271        &r#"
 2272        one
 2273        two
 2274        three
 2275        four
 2276        five
 2277        six
 2278        seven
 2279        eight
 2280        nine
 2281        ˇten
 2282        ˇ"#
 2283        .unindent(),
 2284    );
 2285}
 2286
 2287#[gpui::test]
 2288async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2289    init_test(cx, |_| {});
 2290    let mut cx = EditorTestContext::new(cx).await;
 2291    cx.set_state("one «two threeˇ» four");
 2292    cx.update_editor(|editor, window, cx| {
 2293        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2294        assert_eq!(editor.text(cx), " four");
 2295    });
 2296}
 2297
 2298#[gpui::test]
 2299fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2300    init_test(cx, |_| {});
 2301
 2302    let editor = cx.add_window(|window, cx| {
 2303        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2304        build_editor(buffer.clone(), window, cx)
 2305    });
 2306
 2307    _ = editor.update(cx, |editor, window, cx| {
 2308        editor.change_selections(None, window, cx, |s| {
 2309            s.select_display_ranges([
 2310                // an empty selection - the preceding word fragment is deleted
 2311                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2312                // characters selected - they are deleted
 2313                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2314            ])
 2315        });
 2316        editor.delete_to_previous_word_start(
 2317            &DeleteToPreviousWordStart {
 2318                ignore_newlines: false,
 2319            },
 2320            window,
 2321            cx,
 2322        );
 2323        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2324    });
 2325
 2326    _ = editor.update(cx, |editor, window, cx| {
 2327        editor.change_selections(None, window, cx, |s| {
 2328            s.select_display_ranges([
 2329                // an empty selection - the following word fragment is deleted
 2330                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2331                // characters selected - they are deleted
 2332                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2333            ])
 2334        });
 2335        editor.delete_to_next_word_end(
 2336            &DeleteToNextWordEnd {
 2337                ignore_newlines: false,
 2338            },
 2339            window,
 2340            cx,
 2341        );
 2342        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2343    });
 2344}
 2345
 2346#[gpui::test]
 2347fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2348    init_test(cx, |_| {});
 2349
 2350    let editor = cx.add_window(|window, cx| {
 2351        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2352        build_editor(buffer.clone(), window, cx)
 2353    });
 2354    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2355        ignore_newlines: false,
 2356    };
 2357    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2358        ignore_newlines: true,
 2359    };
 2360
 2361    _ = editor.update(cx, |editor, window, cx| {
 2362        editor.change_selections(None, window, cx, |s| {
 2363            s.select_display_ranges([
 2364                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2365            ])
 2366        });
 2367        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2368        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2369        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2370        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2371        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2372        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2373        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2374        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2375        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2376        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2377        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2378        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2379    });
 2380}
 2381
 2382#[gpui::test]
 2383fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2384    init_test(cx, |_| {});
 2385
 2386    let editor = cx.add_window(|window, cx| {
 2387        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2388        build_editor(buffer.clone(), window, cx)
 2389    });
 2390    let del_to_next_word_end = DeleteToNextWordEnd {
 2391        ignore_newlines: false,
 2392    };
 2393    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2394        ignore_newlines: true,
 2395    };
 2396
 2397    _ = editor.update(cx, |editor, window, cx| {
 2398        editor.change_selections(None, window, cx, |s| {
 2399            s.select_display_ranges([
 2400                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2401            ])
 2402        });
 2403        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2404        assert_eq!(
 2405            editor.buffer.read(cx).read(cx).text(),
 2406            "one\n   two\nthree\n   four"
 2407        );
 2408        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2409        assert_eq!(
 2410            editor.buffer.read(cx).read(cx).text(),
 2411            "\n   two\nthree\n   four"
 2412        );
 2413        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2414        assert_eq!(
 2415            editor.buffer.read(cx).read(cx).text(),
 2416            "two\nthree\n   four"
 2417        );
 2418        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2419        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2420        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2421        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2422        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2423        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2424    });
 2425}
 2426
 2427#[gpui::test]
 2428fn test_newline(cx: &mut TestAppContext) {
 2429    init_test(cx, |_| {});
 2430
 2431    let editor = cx.add_window(|window, cx| {
 2432        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2433        build_editor(buffer.clone(), window, cx)
 2434    });
 2435
 2436    _ = editor.update(cx, |editor, window, cx| {
 2437        editor.change_selections(None, window, cx, |s| {
 2438            s.select_display_ranges([
 2439                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2440                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2441                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2442            ])
 2443        });
 2444
 2445        editor.newline(&Newline, window, cx);
 2446        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2447    });
 2448}
 2449
 2450#[gpui::test]
 2451fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2452    init_test(cx, |_| {});
 2453
 2454    let editor = cx.add_window(|window, cx| {
 2455        let buffer = MultiBuffer::build_simple(
 2456            "
 2457                a
 2458                b(
 2459                    X
 2460                )
 2461                c(
 2462                    X
 2463                )
 2464            "
 2465            .unindent()
 2466            .as_str(),
 2467            cx,
 2468        );
 2469        let mut editor = build_editor(buffer.clone(), window, cx);
 2470        editor.change_selections(None, window, cx, |s| {
 2471            s.select_ranges([
 2472                Point::new(2, 4)..Point::new(2, 5),
 2473                Point::new(5, 4)..Point::new(5, 5),
 2474            ])
 2475        });
 2476        editor
 2477    });
 2478
 2479    _ = editor.update(cx, |editor, window, cx| {
 2480        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2481        editor.buffer.update(cx, |buffer, cx| {
 2482            buffer.edit(
 2483                [
 2484                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2485                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2486                ],
 2487                None,
 2488                cx,
 2489            );
 2490            assert_eq!(
 2491                buffer.read(cx).text(),
 2492                "
 2493                    a
 2494                    b()
 2495                    c()
 2496                "
 2497                .unindent()
 2498            );
 2499        });
 2500        assert_eq!(
 2501            editor.selections.ranges(cx),
 2502            &[
 2503                Point::new(1, 2)..Point::new(1, 2),
 2504                Point::new(2, 2)..Point::new(2, 2),
 2505            ],
 2506        );
 2507
 2508        editor.newline(&Newline, window, cx);
 2509        assert_eq!(
 2510            editor.text(cx),
 2511            "
 2512                a
 2513                b(
 2514                )
 2515                c(
 2516                )
 2517            "
 2518            .unindent()
 2519        );
 2520
 2521        // The selections are moved after the inserted newlines
 2522        assert_eq!(
 2523            editor.selections.ranges(cx),
 2524            &[
 2525                Point::new(2, 0)..Point::new(2, 0),
 2526                Point::new(4, 0)..Point::new(4, 0),
 2527            ],
 2528        );
 2529    });
 2530}
 2531
 2532#[gpui::test]
 2533async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2534    init_test(cx, |settings| {
 2535        settings.defaults.tab_size = NonZeroU32::new(4)
 2536    });
 2537
 2538    let language = Arc::new(
 2539        Language::new(
 2540            LanguageConfig::default(),
 2541            Some(tree_sitter_rust::LANGUAGE.into()),
 2542        )
 2543        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2544        .unwrap(),
 2545    );
 2546
 2547    let mut cx = EditorTestContext::new(cx).await;
 2548    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2549    cx.set_state(indoc! {"
 2550        const a: ˇA = (
 2551 2552                «const_functionˇ»(ˇ),
 2553                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2554 2555        ˇ);ˇ
 2556    "});
 2557
 2558    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2559    cx.assert_editor_state(indoc! {"
 2560        ˇ
 2561        const a: A = (
 2562            ˇ
 2563            (
 2564                ˇ
 2565                ˇ
 2566                const_function(),
 2567                ˇ
 2568                ˇ
 2569                ˇ
 2570                ˇ
 2571                something_else,
 2572                ˇ
 2573            )
 2574            ˇ
 2575            ˇ
 2576        );
 2577    "});
 2578}
 2579
 2580#[gpui::test]
 2581async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2582    init_test(cx, |settings| {
 2583        settings.defaults.tab_size = NonZeroU32::new(4)
 2584    });
 2585
 2586    let language = Arc::new(
 2587        Language::new(
 2588            LanguageConfig::default(),
 2589            Some(tree_sitter_rust::LANGUAGE.into()),
 2590        )
 2591        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2592        .unwrap(),
 2593    );
 2594
 2595    let mut cx = EditorTestContext::new(cx).await;
 2596    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2597    cx.set_state(indoc! {"
 2598        const a: ˇA = (
 2599 2600                «const_functionˇ»(ˇ),
 2601                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2602 2603        ˇ);ˇ
 2604    "});
 2605
 2606    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2607    cx.assert_editor_state(indoc! {"
 2608        const a: A = (
 2609            ˇ
 2610            (
 2611                ˇ
 2612                const_function(),
 2613                ˇ
 2614                ˇ
 2615                something_else,
 2616                ˇ
 2617                ˇ
 2618                ˇ
 2619                ˇ
 2620            )
 2621            ˇ
 2622        );
 2623        ˇ
 2624        ˇ
 2625    "});
 2626}
 2627
 2628#[gpui::test]
 2629async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2630    init_test(cx, |settings| {
 2631        settings.defaults.tab_size = NonZeroU32::new(4)
 2632    });
 2633
 2634    let language = Arc::new(Language::new(
 2635        LanguageConfig {
 2636            line_comments: vec!["//".into()],
 2637            ..LanguageConfig::default()
 2638        },
 2639        None,
 2640    ));
 2641    {
 2642        let mut cx = EditorTestContext::new(cx).await;
 2643        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2644        cx.set_state(indoc! {"
 2645        // Fooˇ
 2646    "});
 2647
 2648        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2649        cx.assert_editor_state(indoc! {"
 2650        // Foo
 2651        //ˇ
 2652    "});
 2653        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2654        cx.set_state(indoc! {"
 2655        ˇ// Foo
 2656    "});
 2657        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2658        cx.assert_editor_state(indoc! {"
 2659
 2660        ˇ// Foo
 2661    "});
 2662    }
 2663    // Ensure that comment continuations can be disabled.
 2664    update_test_language_settings(cx, |settings| {
 2665        settings.defaults.extend_comment_on_newline = Some(false);
 2666    });
 2667    let mut cx = EditorTestContext::new(cx).await;
 2668    cx.set_state(indoc! {"
 2669        // Fooˇ
 2670    "});
 2671    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2672    cx.assert_editor_state(indoc! {"
 2673        // Foo
 2674        ˇ
 2675    "});
 2676}
 2677
 2678#[gpui::test]
 2679fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2680    init_test(cx, |_| {});
 2681
 2682    let editor = cx.add_window(|window, cx| {
 2683        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2684        let mut editor = build_editor(buffer.clone(), window, cx);
 2685        editor.change_selections(None, window, cx, |s| {
 2686            s.select_ranges([3..4, 11..12, 19..20])
 2687        });
 2688        editor
 2689    });
 2690
 2691    _ = editor.update(cx, |editor, window, cx| {
 2692        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2693        editor.buffer.update(cx, |buffer, cx| {
 2694            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2695            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2696        });
 2697        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2698
 2699        editor.insert("Z", window, cx);
 2700        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2701
 2702        // The selections are moved after the inserted characters
 2703        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2704    });
 2705}
 2706
 2707#[gpui::test]
 2708async fn test_tab(cx: &mut gpui::TestAppContext) {
 2709    init_test(cx, |settings| {
 2710        settings.defaults.tab_size = NonZeroU32::new(3)
 2711    });
 2712
 2713    let mut cx = EditorTestContext::new(cx).await;
 2714    cx.set_state(indoc! {"
 2715        ˇabˇc
 2716        ˇ🏀ˇ🏀ˇefg
 2717 2718    "});
 2719    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2720    cx.assert_editor_state(indoc! {"
 2721           ˇab ˇc
 2722           ˇ🏀  ˇ🏀  ˇefg
 2723        d  ˇ
 2724    "});
 2725
 2726    cx.set_state(indoc! {"
 2727        a
 2728        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2729    "});
 2730    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2731    cx.assert_editor_state(indoc! {"
 2732        a
 2733           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2734    "});
 2735}
 2736
 2737#[gpui::test]
 2738async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2739    init_test(cx, |_| {});
 2740
 2741    let mut cx = EditorTestContext::new(cx).await;
 2742    let language = Arc::new(
 2743        Language::new(
 2744            LanguageConfig::default(),
 2745            Some(tree_sitter_rust::LANGUAGE.into()),
 2746        )
 2747        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2748        .unwrap(),
 2749    );
 2750    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2751
 2752    // cursors that are already at the suggested indent level insert
 2753    // a soft tab. cursors that are to the left of the suggested indent
 2754    // auto-indent their line.
 2755    cx.set_state(indoc! {"
 2756        ˇ
 2757        const a: B = (
 2758            c(
 2759                d(
 2760        ˇ
 2761                )
 2762        ˇ
 2763        ˇ    )
 2764        );
 2765    "});
 2766    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2767    cx.assert_editor_state(indoc! {"
 2768            ˇ
 2769        const a: B = (
 2770            c(
 2771                d(
 2772                    ˇ
 2773                )
 2774                ˇ
 2775            ˇ)
 2776        );
 2777    "});
 2778
 2779    // handle auto-indent when there are multiple cursors on the same line
 2780    cx.set_state(indoc! {"
 2781        const a: B = (
 2782            c(
 2783        ˇ    ˇ
 2784        ˇ    )
 2785        );
 2786    "});
 2787    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2788    cx.assert_editor_state(indoc! {"
 2789        const a: B = (
 2790            c(
 2791                ˇ
 2792            ˇ)
 2793        );
 2794    "});
 2795}
 2796
 2797#[gpui::test]
 2798async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2799    init_test(cx, |settings| {
 2800        settings.defaults.tab_size = NonZeroU32::new(4)
 2801    });
 2802
 2803    let language = Arc::new(
 2804        Language::new(
 2805            LanguageConfig::default(),
 2806            Some(tree_sitter_rust::LANGUAGE.into()),
 2807        )
 2808        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2809        .unwrap(),
 2810    );
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2814    cx.set_state(indoc! {"
 2815        fn a() {
 2816            if b {
 2817        \t ˇc
 2818            }
 2819        }
 2820    "});
 2821
 2822    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2823    cx.assert_editor_state(indoc! {"
 2824        fn a() {
 2825            if b {
 2826                ˇc
 2827            }
 2828        }
 2829    "});
 2830}
 2831
 2832#[gpui::test]
 2833async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2834    init_test(cx, |settings| {
 2835        settings.defaults.tab_size = NonZeroU32::new(4);
 2836    });
 2837
 2838    let mut cx = EditorTestContext::new(cx).await;
 2839
 2840    cx.set_state(indoc! {"
 2841          «oneˇ» «twoˇ»
 2842        three
 2843         four
 2844    "});
 2845    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2846    cx.assert_editor_state(indoc! {"
 2847            «oneˇ» «twoˇ»
 2848        three
 2849         four
 2850    "});
 2851
 2852    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        «oneˇ» «twoˇ»
 2855        three
 2856         four
 2857    "});
 2858
 2859    // select across line ending
 2860    cx.set_state(indoc! {"
 2861        one two
 2862        t«hree
 2863        ˇ» four
 2864    "});
 2865    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2866    cx.assert_editor_state(indoc! {"
 2867        one two
 2868            t«hree
 2869        ˇ» four
 2870    "});
 2871
 2872    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2873    cx.assert_editor_state(indoc! {"
 2874        one two
 2875        t«hree
 2876        ˇ» four
 2877    "});
 2878
 2879    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2880    cx.set_state(indoc! {"
 2881        one two
 2882        ˇthree
 2883            four
 2884    "});
 2885    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2886    cx.assert_editor_state(indoc! {"
 2887        one two
 2888            ˇthree
 2889            four
 2890    "});
 2891
 2892    cx.set_state(indoc! {"
 2893        one two
 2894        ˇ    three
 2895            four
 2896    "});
 2897    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2898    cx.assert_editor_state(indoc! {"
 2899        one two
 2900        ˇthree
 2901            four
 2902    "});
 2903}
 2904
 2905#[gpui::test]
 2906async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2907    init_test(cx, |settings| {
 2908        settings.defaults.hard_tabs = Some(true);
 2909    });
 2910
 2911    let mut cx = EditorTestContext::new(cx).await;
 2912
 2913    // select two ranges on one line
 2914    cx.set_state(indoc! {"
 2915        «oneˇ» «twoˇ»
 2916        three
 2917        four
 2918    "});
 2919    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2920    cx.assert_editor_state(indoc! {"
 2921        \t«oneˇ» «twoˇ»
 2922        three
 2923        four
 2924    "});
 2925    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2926    cx.assert_editor_state(indoc! {"
 2927        \t\t«oneˇ» «twoˇ»
 2928        three
 2929        four
 2930    "});
 2931    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2932    cx.assert_editor_state(indoc! {"
 2933        \t«oneˇ» «twoˇ»
 2934        three
 2935        four
 2936    "});
 2937    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2938    cx.assert_editor_state(indoc! {"
 2939        «oneˇ» «twoˇ»
 2940        three
 2941        four
 2942    "});
 2943
 2944    // select across a line ending
 2945    cx.set_state(indoc! {"
 2946        one two
 2947        t«hree
 2948        ˇ»four
 2949    "});
 2950    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2951    cx.assert_editor_state(indoc! {"
 2952        one two
 2953        \tt«hree
 2954        ˇ»four
 2955    "});
 2956    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2957    cx.assert_editor_state(indoc! {"
 2958        one two
 2959        \t\tt«hree
 2960        ˇ»four
 2961    "});
 2962    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2963    cx.assert_editor_state(indoc! {"
 2964        one two
 2965        \tt«hree
 2966        ˇ»four
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        one two
 2971        t«hree
 2972        ˇ»four
 2973    "});
 2974
 2975    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2976    cx.set_state(indoc! {"
 2977        one two
 2978        ˇthree
 2979        four
 2980    "});
 2981    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2982    cx.assert_editor_state(indoc! {"
 2983        one two
 2984        ˇthree
 2985        four
 2986    "});
 2987    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2988    cx.assert_editor_state(indoc! {"
 2989        one two
 2990        \tˇthree
 2991        four
 2992    "});
 2993    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2994    cx.assert_editor_state(indoc! {"
 2995        one two
 2996        ˇthree
 2997        four
 2998    "});
 2999}
 3000
 3001#[gpui::test]
 3002fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3003    init_test(cx, |settings| {
 3004        settings.languages.extend([
 3005            (
 3006                "TOML".into(),
 3007                LanguageSettingsContent {
 3008                    tab_size: NonZeroU32::new(2),
 3009                    ..Default::default()
 3010                },
 3011            ),
 3012            (
 3013                "Rust".into(),
 3014                LanguageSettingsContent {
 3015                    tab_size: NonZeroU32::new(4),
 3016                    ..Default::default()
 3017                },
 3018            ),
 3019        ]);
 3020    });
 3021
 3022    let toml_language = Arc::new(Language::new(
 3023        LanguageConfig {
 3024            name: "TOML".into(),
 3025            ..Default::default()
 3026        },
 3027        None,
 3028    ));
 3029    let rust_language = Arc::new(Language::new(
 3030        LanguageConfig {
 3031            name: "Rust".into(),
 3032            ..Default::default()
 3033        },
 3034        None,
 3035    ));
 3036
 3037    let toml_buffer =
 3038        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3039    let rust_buffer =
 3040        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3041    let multibuffer = cx.new(|cx| {
 3042        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3043        multibuffer.push_excerpts(
 3044            toml_buffer.clone(),
 3045            [ExcerptRange {
 3046                context: Point::new(0, 0)..Point::new(2, 0),
 3047                primary: None,
 3048            }],
 3049            cx,
 3050        );
 3051        multibuffer.push_excerpts(
 3052            rust_buffer.clone(),
 3053            [ExcerptRange {
 3054                context: Point::new(0, 0)..Point::new(1, 0),
 3055                primary: None,
 3056            }],
 3057            cx,
 3058        );
 3059        multibuffer
 3060    });
 3061
 3062    cx.add_window(|window, cx| {
 3063        let mut editor = build_editor(multibuffer, window, cx);
 3064
 3065        assert_eq!(
 3066            editor.text(cx),
 3067            indoc! {"
 3068                a = 1
 3069                b = 2
 3070
 3071                const c: usize = 3;
 3072            "}
 3073        );
 3074
 3075        select_ranges(
 3076            &mut editor,
 3077            indoc! {"
 3078                «aˇ» = 1
 3079                b = 2
 3080
 3081                «const c:ˇ» usize = 3;
 3082            "},
 3083            window,
 3084            cx,
 3085        );
 3086
 3087        editor.tab(&Tab, window, cx);
 3088        assert_text_with_selections(
 3089            &mut editor,
 3090            indoc! {"
 3091                  «aˇ» = 1
 3092                b = 2
 3093
 3094                    «const c:ˇ» usize = 3;
 3095            "},
 3096            cx,
 3097        );
 3098        editor.tab_prev(&TabPrev, window, cx);
 3099        assert_text_with_selections(
 3100            &mut editor,
 3101            indoc! {"
 3102                «aˇ» = 1
 3103                b = 2
 3104
 3105                «const c:ˇ» usize = 3;
 3106            "},
 3107            cx,
 3108        );
 3109
 3110        editor
 3111    });
 3112}
 3113
 3114#[gpui::test]
 3115async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3116    init_test(cx, |_| {});
 3117
 3118    let mut cx = EditorTestContext::new(cx).await;
 3119
 3120    // Basic backspace
 3121    cx.set_state(indoc! {"
 3122        onˇe two three
 3123        fou«rˇ» five six
 3124        seven «ˇeight nine
 3125        »ten
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        oˇe two three
 3130        fouˇ five six
 3131        seven ˇten
 3132    "});
 3133
 3134    // Test backspace inside and around indents
 3135    cx.set_state(indoc! {"
 3136        zero
 3137            ˇone
 3138                ˇtwo
 3139            ˇ ˇ ˇ  three
 3140        ˇ  ˇ  four
 3141    "});
 3142    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3143    cx.assert_editor_state(indoc! {"
 3144        zero
 3145        ˇone
 3146            ˇtwo
 3147        ˇ  threeˇ  four
 3148    "});
 3149
 3150    // Test backspace with line_mode set to true
 3151    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3152    cx.set_state(indoc! {"
 3153        The ˇquick ˇbrown
 3154        fox jumps over
 3155        the lazy dog
 3156        ˇThe qu«ick bˇ»rown"});
 3157    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3158    cx.assert_editor_state(indoc! {"
 3159        ˇfox jumps over
 3160        the lazy dogˇ"});
 3161}
 3162
 3163#[gpui::test]
 3164async fn test_delete(cx: &mut gpui::TestAppContext) {
 3165    init_test(cx, |_| {});
 3166
 3167    let mut cx = EditorTestContext::new(cx).await;
 3168    cx.set_state(indoc! {"
 3169        onˇe two three
 3170        fou«rˇ» five six
 3171        seven «ˇeight nine
 3172        »ten
 3173    "});
 3174    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3175    cx.assert_editor_state(indoc! {"
 3176        onˇ two three
 3177        fouˇ five six
 3178        seven ˇten
 3179    "});
 3180
 3181    // Test backspace with line_mode set to true
 3182    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3183    cx.set_state(indoc! {"
 3184        The ˇquick ˇbrown
 3185        fox «ˇjum»ps over
 3186        the lazy dog
 3187        ˇThe qu«ick bˇ»rown"});
 3188    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3189    cx.assert_editor_state("ˇthe lazy dogˇ");
 3190}
 3191
 3192#[gpui::test]
 3193fn test_delete_line(cx: &mut TestAppContext) {
 3194    init_test(cx, |_| {});
 3195
 3196    let editor = cx.add_window(|window, cx| {
 3197        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3198        build_editor(buffer, window, cx)
 3199    });
 3200    _ = editor.update(cx, |editor, window, cx| {
 3201        editor.change_selections(None, window, cx, |s| {
 3202            s.select_display_ranges([
 3203                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3204                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3205                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3206            ])
 3207        });
 3208        editor.delete_line(&DeleteLine, window, cx);
 3209        assert_eq!(editor.display_text(cx), "ghi");
 3210        assert_eq!(
 3211            editor.selections.display_ranges(cx),
 3212            vec![
 3213                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3214                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3215            ]
 3216        );
 3217    });
 3218
 3219    let editor = cx.add_window(|window, cx| {
 3220        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3221        build_editor(buffer, window, cx)
 3222    });
 3223    _ = editor.update(cx, |editor, window, cx| {
 3224        editor.change_selections(None, window, cx, |s| {
 3225            s.select_display_ranges([
 3226                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3227            ])
 3228        });
 3229        editor.delete_line(&DeleteLine, window, cx);
 3230        assert_eq!(editor.display_text(cx), "ghi\n");
 3231        assert_eq!(
 3232            editor.selections.display_ranges(cx),
 3233            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3234        );
 3235    });
 3236}
 3237
 3238#[gpui::test]
 3239fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3240    init_test(cx, |_| {});
 3241
 3242    cx.add_window(|window, cx| {
 3243        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3244        let mut editor = build_editor(buffer.clone(), window, cx);
 3245        let buffer = buffer.read(cx).as_singleton().unwrap();
 3246
 3247        assert_eq!(
 3248            editor.selections.ranges::<Point>(cx),
 3249            &[Point::new(0, 0)..Point::new(0, 0)]
 3250        );
 3251
 3252        // When on single line, replace newline at end by space
 3253        editor.join_lines(&JoinLines, window, cx);
 3254        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3255        assert_eq!(
 3256            editor.selections.ranges::<Point>(cx),
 3257            &[Point::new(0, 3)..Point::new(0, 3)]
 3258        );
 3259
 3260        // When multiple lines are selected, remove newlines that are spanned by the selection
 3261        editor.change_selections(None, window, cx, |s| {
 3262            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3263        });
 3264        editor.join_lines(&JoinLines, window, cx);
 3265        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3266        assert_eq!(
 3267            editor.selections.ranges::<Point>(cx),
 3268            &[Point::new(0, 11)..Point::new(0, 11)]
 3269        );
 3270
 3271        // Undo should be transactional
 3272        editor.undo(&Undo, window, cx);
 3273        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3274        assert_eq!(
 3275            editor.selections.ranges::<Point>(cx),
 3276            &[Point::new(0, 5)..Point::new(2, 2)]
 3277        );
 3278
 3279        // When joining an empty line don't insert a space
 3280        editor.change_selections(None, window, cx, |s| {
 3281            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3282        });
 3283        editor.join_lines(&JoinLines, window, cx);
 3284        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3285        assert_eq!(
 3286            editor.selections.ranges::<Point>(cx),
 3287            [Point::new(2, 3)..Point::new(2, 3)]
 3288        );
 3289
 3290        // We can remove trailing newlines
 3291        editor.join_lines(&JoinLines, window, cx);
 3292        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3293        assert_eq!(
 3294            editor.selections.ranges::<Point>(cx),
 3295            [Point::new(2, 3)..Point::new(2, 3)]
 3296        );
 3297
 3298        // We don't blow up on the last line
 3299        editor.join_lines(&JoinLines, window, cx);
 3300        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3301        assert_eq!(
 3302            editor.selections.ranges::<Point>(cx),
 3303            [Point::new(2, 3)..Point::new(2, 3)]
 3304        );
 3305
 3306        // reset to test indentation
 3307        editor.buffer.update(cx, |buffer, cx| {
 3308            buffer.edit(
 3309                [
 3310                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3311                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3312                ],
 3313                None,
 3314                cx,
 3315            )
 3316        });
 3317
 3318        // We remove any leading spaces
 3319        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3320        editor.change_selections(None, window, cx, |s| {
 3321            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3322        });
 3323        editor.join_lines(&JoinLines, window, cx);
 3324        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3325
 3326        // We don't insert a space for a line containing only spaces
 3327        editor.join_lines(&JoinLines, window, cx);
 3328        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3329
 3330        // We ignore any leading tabs
 3331        editor.join_lines(&JoinLines, window, cx);
 3332        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3333
 3334        editor
 3335    });
 3336}
 3337
 3338#[gpui::test]
 3339fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3340    init_test(cx, |_| {});
 3341
 3342    cx.add_window(|window, cx| {
 3343        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3344        let mut editor = build_editor(buffer.clone(), window, cx);
 3345        let buffer = buffer.read(cx).as_singleton().unwrap();
 3346
 3347        editor.change_selections(None, window, cx, |s| {
 3348            s.select_ranges([
 3349                Point::new(0, 2)..Point::new(1, 1),
 3350                Point::new(1, 2)..Point::new(1, 2),
 3351                Point::new(3, 1)..Point::new(3, 2),
 3352            ])
 3353        });
 3354
 3355        editor.join_lines(&JoinLines, window, cx);
 3356        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3357
 3358        assert_eq!(
 3359            editor.selections.ranges::<Point>(cx),
 3360            [
 3361                Point::new(0, 7)..Point::new(0, 7),
 3362                Point::new(1, 3)..Point::new(1, 3)
 3363            ]
 3364        );
 3365        editor
 3366    });
 3367}
 3368
 3369#[gpui::test]
 3370async fn test_join_lines_with_git_diff_base(
 3371    executor: BackgroundExecutor,
 3372    cx: &mut gpui::TestAppContext,
 3373) {
 3374    init_test(cx, |_| {});
 3375
 3376    let mut cx = EditorTestContext::new(cx).await;
 3377
 3378    let diff_base = r#"
 3379        Line 0
 3380        Line 1
 3381        Line 2
 3382        Line 3
 3383        "#
 3384    .unindent();
 3385
 3386    cx.set_state(
 3387        &r#"
 3388        ˇLine 0
 3389        Line 1
 3390        Line 2
 3391        Line 3
 3392        "#
 3393        .unindent(),
 3394    );
 3395
 3396    cx.set_diff_base(&diff_base);
 3397    executor.run_until_parked();
 3398
 3399    // Join lines
 3400    cx.update_editor(|editor, window, cx| {
 3401        editor.join_lines(&JoinLines, window, cx);
 3402    });
 3403    executor.run_until_parked();
 3404
 3405    cx.assert_editor_state(
 3406        &r#"
 3407        Line 0ˇ Line 1
 3408        Line 2
 3409        Line 3
 3410        "#
 3411        .unindent(),
 3412    );
 3413    // Join again
 3414    cx.update_editor(|editor, window, cx| {
 3415        editor.join_lines(&JoinLines, window, cx);
 3416    });
 3417    executor.run_until_parked();
 3418
 3419    cx.assert_editor_state(
 3420        &r#"
 3421        Line 0 Line 1ˇ Line 2
 3422        Line 3
 3423        "#
 3424        .unindent(),
 3425    );
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_custom_newlines_cause_no_false_positive_diffs(
 3430    executor: BackgroundExecutor,
 3431    cx: &mut gpui::TestAppContext,
 3432) {
 3433    init_test(cx, |_| {});
 3434    let mut cx = EditorTestContext::new(cx).await;
 3435    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3436    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3437    executor.run_until_parked();
 3438
 3439    cx.update_editor(|editor, window, cx| {
 3440        let snapshot = editor.snapshot(window, cx);
 3441        assert_eq!(
 3442            snapshot
 3443                .buffer_snapshot
 3444                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3445                .collect::<Vec<_>>(),
 3446            Vec::new(),
 3447            "Should not have any diffs for files with custom newlines"
 3448        );
 3449    });
 3450}
 3451
 3452#[gpui::test]
 3453async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3454    init_test(cx, |_| {});
 3455
 3456    let mut cx = EditorTestContext::new(cx).await;
 3457
 3458    // Test sort_lines_case_insensitive()
 3459    cx.set_state(indoc! {"
 3460        «z
 3461        y
 3462        x
 3463        Z
 3464        Y
 3465        Xˇ»
 3466    "});
 3467    cx.update_editor(|e, window, cx| {
 3468        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3469    });
 3470    cx.assert_editor_state(indoc! {"
 3471        «x
 3472        X
 3473        y
 3474        Y
 3475        z
 3476        Zˇ»
 3477    "});
 3478
 3479    // Test reverse_lines()
 3480    cx.set_state(indoc! {"
 3481        «5
 3482        4
 3483        3
 3484        2
 3485        1ˇ»
 3486    "});
 3487    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3488    cx.assert_editor_state(indoc! {"
 3489        «1
 3490        2
 3491        3
 3492        4
 3493        5ˇ»
 3494    "});
 3495
 3496    // Skip testing shuffle_line()
 3497
 3498    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3499    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3500
 3501    // Don't manipulate when cursor is on single line, but expand the selection
 3502    cx.set_state(indoc! {"
 3503        ddˇdd
 3504        ccc
 3505        bb
 3506        a
 3507    "});
 3508    cx.update_editor(|e, window, cx| {
 3509        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3510    });
 3511    cx.assert_editor_state(indoc! {"
 3512        «ddddˇ»
 3513        ccc
 3514        bb
 3515        a
 3516    "});
 3517
 3518    // Basic manipulate case
 3519    // Start selection moves to column 0
 3520    // End of selection shrinks to fit shorter line
 3521    cx.set_state(indoc! {"
 3522        dd«d
 3523        ccc
 3524        bb
 3525        aaaaaˇ»
 3526    "});
 3527    cx.update_editor(|e, window, cx| {
 3528        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3529    });
 3530    cx.assert_editor_state(indoc! {"
 3531        «aaaaa
 3532        bb
 3533        ccc
 3534        dddˇ»
 3535    "});
 3536
 3537    // Manipulate case with newlines
 3538    cx.set_state(indoc! {"
 3539        dd«d
 3540        ccc
 3541
 3542        bb
 3543        aaaaa
 3544
 3545        ˇ»
 3546    "});
 3547    cx.update_editor(|e, window, cx| {
 3548        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3549    });
 3550    cx.assert_editor_state(indoc! {"
 3551        «
 3552
 3553        aaaaa
 3554        bb
 3555        ccc
 3556        dddˇ»
 3557
 3558    "});
 3559
 3560    // Adding new line
 3561    cx.set_state(indoc! {"
 3562        aa«a
 3563        bbˇ»b
 3564    "});
 3565    cx.update_editor(|e, window, cx| {
 3566        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3567    });
 3568    cx.assert_editor_state(indoc! {"
 3569        «aaa
 3570        bbb
 3571        added_lineˇ»
 3572    "});
 3573
 3574    // Removing line
 3575    cx.set_state(indoc! {"
 3576        aa«a
 3577        bbbˇ»
 3578    "});
 3579    cx.update_editor(|e, window, cx| {
 3580        e.manipulate_lines(window, cx, |lines| {
 3581            lines.pop();
 3582        })
 3583    });
 3584    cx.assert_editor_state(indoc! {"
 3585        «aaaˇ»
 3586    "});
 3587
 3588    // Removing all lines
 3589    cx.set_state(indoc! {"
 3590        aa«a
 3591        bbbˇ»
 3592    "});
 3593    cx.update_editor(|e, window, cx| {
 3594        e.manipulate_lines(window, cx, |lines| {
 3595            lines.drain(..);
 3596        })
 3597    });
 3598    cx.assert_editor_state(indoc! {"
 3599        ˇ
 3600    "});
 3601}
 3602
 3603#[gpui::test]
 3604async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3605    init_test(cx, |_| {});
 3606
 3607    let mut cx = EditorTestContext::new(cx).await;
 3608
 3609    // Consider continuous selection as single selection
 3610    cx.set_state(indoc! {"
 3611        Aaa«aa
 3612        cˇ»c«c
 3613        bb
 3614        aaaˇ»aa
 3615    "});
 3616    cx.update_editor(|e, window, cx| {
 3617        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3618    });
 3619    cx.assert_editor_state(indoc! {"
 3620        «Aaaaa
 3621        ccc
 3622        bb
 3623        aaaaaˇ»
 3624    "});
 3625
 3626    cx.set_state(indoc! {"
 3627        Aaa«aa
 3628        cˇ»c«c
 3629        bb
 3630        aaaˇ»aa
 3631    "});
 3632    cx.update_editor(|e, window, cx| {
 3633        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3634    });
 3635    cx.assert_editor_state(indoc! {"
 3636        «Aaaaa
 3637        ccc
 3638        bbˇ»
 3639    "});
 3640
 3641    // Consider non continuous selection as distinct dedup operations
 3642    cx.set_state(indoc! {"
 3643        «aaaaa
 3644        bb
 3645        aaaaa
 3646        aaaaaˇ»
 3647
 3648        aaa«aaˇ»
 3649    "});
 3650    cx.update_editor(|e, window, cx| {
 3651        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3652    });
 3653    cx.assert_editor_state(indoc! {"
 3654        «aaaaa
 3655        bbˇ»
 3656
 3657        «aaaaaˇ»
 3658    "});
 3659}
 3660
 3661#[gpui::test]
 3662async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3663    init_test(cx, |_| {});
 3664
 3665    let mut cx = EditorTestContext::new(cx).await;
 3666
 3667    cx.set_state(indoc! {"
 3668        «Aaa
 3669        aAa
 3670        Aaaˇ»
 3671    "});
 3672    cx.update_editor(|e, window, cx| {
 3673        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3674    });
 3675    cx.assert_editor_state(indoc! {"
 3676        «Aaa
 3677        aAaˇ»
 3678    "});
 3679
 3680    cx.set_state(indoc! {"
 3681        «Aaa
 3682        aAa
 3683        aaAˇ»
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3687    });
 3688    cx.assert_editor_state(indoc! {"
 3689        «Aaaˇ»
 3690    "});
 3691}
 3692
 3693#[gpui::test]
 3694async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3695    init_test(cx, |_| {});
 3696
 3697    let mut cx = EditorTestContext::new(cx).await;
 3698
 3699    // Manipulate with multiple selections on a single line
 3700    cx.set_state(indoc! {"
 3701        dd«dd
 3702        cˇ»c«c
 3703        bb
 3704        aaaˇ»aa
 3705    "});
 3706    cx.update_editor(|e, window, cx| {
 3707        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3708    });
 3709    cx.assert_editor_state(indoc! {"
 3710        «aaaaa
 3711        bb
 3712        ccc
 3713        ddddˇ»
 3714    "});
 3715
 3716    // Manipulate with multiple disjoin selections
 3717    cx.set_state(indoc! {"
 3718 3719        4
 3720        3
 3721        2
 3722        1ˇ»
 3723
 3724        dd«dd
 3725        ccc
 3726        bb
 3727        aaaˇ»aa
 3728    "});
 3729    cx.update_editor(|e, window, cx| {
 3730        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3731    });
 3732    cx.assert_editor_state(indoc! {"
 3733        «1
 3734        2
 3735        3
 3736        4
 3737        5ˇ»
 3738
 3739        «aaaaa
 3740        bb
 3741        ccc
 3742        ddddˇ»
 3743    "});
 3744
 3745    // Adding lines on each selection
 3746    cx.set_state(indoc! {"
 3747 3748        1ˇ»
 3749
 3750        bb«bb
 3751        aaaˇ»aa
 3752    "});
 3753    cx.update_editor(|e, window, cx| {
 3754        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3755    });
 3756    cx.assert_editor_state(indoc! {"
 3757        «2
 3758        1
 3759        added lineˇ»
 3760
 3761        «bbbb
 3762        aaaaa
 3763        added lineˇ»
 3764    "});
 3765
 3766    // Removing lines on each selection
 3767    cx.set_state(indoc! {"
 3768 3769        1ˇ»
 3770
 3771        bb«bb
 3772        aaaˇ»aa
 3773    "});
 3774    cx.update_editor(|e, window, cx| {
 3775        e.manipulate_lines(window, cx, |lines| {
 3776            lines.pop();
 3777        })
 3778    });
 3779    cx.assert_editor_state(indoc! {"
 3780        «2ˇ»
 3781
 3782        «bbbbˇ»
 3783    "});
 3784}
 3785
 3786#[gpui::test]
 3787async fn test_manipulate_text(cx: &mut TestAppContext) {
 3788    init_test(cx, |_| {});
 3789
 3790    let mut cx = EditorTestContext::new(cx).await;
 3791
 3792    // Test convert_to_upper_case()
 3793    cx.set_state(indoc! {"
 3794        «hello worldˇ»
 3795    "});
 3796    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3797    cx.assert_editor_state(indoc! {"
 3798        «HELLO WORLDˇ»
 3799    "});
 3800
 3801    // Test convert_to_lower_case()
 3802    cx.set_state(indoc! {"
 3803        «HELLO WORLDˇ»
 3804    "});
 3805    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3806    cx.assert_editor_state(indoc! {"
 3807        «hello worldˇ»
 3808    "});
 3809
 3810    // Test multiple line, single selection case
 3811    cx.set_state(indoc! {"
 3812        «The quick brown
 3813        fox jumps over
 3814        the lazy dogˇ»
 3815    "});
 3816    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3817    cx.assert_editor_state(indoc! {"
 3818        «The Quick Brown
 3819        Fox Jumps Over
 3820        The Lazy Dogˇ»
 3821    "});
 3822
 3823    // Test multiple line, single selection case
 3824    cx.set_state(indoc! {"
 3825        «The quick brown
 3826        fox jumps over
 3827        the lazy dogˇ»
 3828    "});
 3829    cx.update_editor(|e, window, cx| {
 3830        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3831    });
 3832    cx.assert_editor_state(indoc! {"
 3833        «TheQuickBrown
 3834        FoxJumpsOver
 3835        TheLazyDogˇ»
 3836    "});
 3837
 3838    // From here on out, test more complex cases of manipulate_text()
 3839
 3840    // Test no selection case - should affect words cursors are in
 3841    // Cursor at beginning, middle, and end of word
 3842    cx.set_state(indoc! {"
 3843        ˇhello big beauˇtiful worldˇ
 3844    "});
 3845    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3846    cx.assert_editor_state(indoc! {"
 3847        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3848    "});
 3849
 3850    // Test multiple selections on a single line and across multiple lines
 3851    cx.set_state(indoc! {"
 3852        «Theˇ» quick «brown
 3853        foxˇ» jumps «overˇ»
 3854        the «lazyˇ» dog
 3855    "});
 3856    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3857    cx.assert_editor_state(indoc! {"
 3858        «THEˇ» quick «BROWN
 3859        FOXˇ» jumps «OVERˇ»
 3860        the «LAZYˇ» dog
 3861    "});
 3862
 3863    // Test case where text length grows
 3864    cx.set_state(indoc! {"
 3865        «tschüߡ»
 3866    "});
 3867    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3868    cx.assert_editor_state(indoc! {"
 3869        «TSCHÜSSˇ»
 3870    "});
 3871
 3872    // Test to make sure we don't crash when text shrinks
 3873    cx.set_state(indoc! {"
 3874        aaa_bbbˇ
 3875    "});
 3876    cx.update_editor(|e, window, cx| {
 3877        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3878    });
 3879    cx.assert_editor_state(indoc! {"
 3880        «aaaBbbˇ»
 3881    "});
 3882
 3883    // Test to make sure we all aware of the fact that each word can grow and shrink
 3884    // Final selections should be aware of this fact
 3885    cx.set_state(indoc! {"
 3886        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3887    "});
 3888    cx.update_editor(|e, window, cx| {
 3889        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3890    });
 3891    cx.assert_editor_state(indoc! {"
 3892        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3893    "});
 3894
 3895    cx.set_state(indoc! {"
 3896        «hElLo, WoRld!ˇ»
 3897    "});
 3898    cx.update_editor(|e, window, cx| {
 3899        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3900    });
 3901    cx.assert_editor_state(indoc! {"
 3902        «HeLlO, wOrLD!ˇ»
 3903    "});
 3904}
 3905
 3906#[gpui::test]
 3907fn test_duplicate_line(cx: &mut TestAppContext) {
 3908    init_test(cx, |_| {});
 3909
 3910    let editor = cx.add_window(|window, cx| {
 3911        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3912        build_editor(buffer, window, cx)
 3913    });
 3914    _ = editor.update(cx, |editor, window, cx| {
 3915        editor.change_selections(None, window, cx, |s| {
 3916            s.select_display_ranges([
 3917                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3918                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3919                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3920                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3921            ])
 3922        });
 3923        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3924        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3925        assert_eq!(
 3926            editor.selections.display_ranges(cx),
 3927            vec![
 3928                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3929                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3930                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3931                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3932            ]
 3933        );
 3934    });
 3935
 3936    let editor = cx.add_window(|window, cx| {
 3937        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3938        build_editor(buffer, window, cx)
 3939    });
 3940    _ = editor.update(cx, |editor, window, cx| {
 3941        editor.change_selections(None, window, cx, |s| {
 3942            s.select_display_ranges([
 3943                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3944                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3945            ])
 3946        });
 3947        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3948        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3949        assert_eq!(
 3950            editor.selections.display_ranges(cx),
 3951            vec![
 3952                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3953                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3954            ]
 3955        );
 3956    });
 3957
 3958    // With `move_upwards` the selections stay in place, except for
 3959    // the lines inserted above them
 3960    let editor = cx.add_window(|window, cx| {
 3961        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3962        build_editor(buffer, window, cx)
 3963    });
 3964    _ = editor.update(cx, |editor, window, cx| {
 3965        editor.change_selections(None, window, cx, |s| {
 3966            s.select_display_ranges([
 3967                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3968                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3969                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3970                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3971            ])
 3972        });
 3973        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3974        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3975        assert_eq!(
 3976            editor.selections.display_ranges(cx),
 3977            vec![
 3978                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3979                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3980                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3981                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3982            ]
 3983        );
 3984    });
 3985
 3986    let editor = cx.add_window(|window, cx| {
 3987        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3988        build_editor(buffer, window, cx)
 3989    });
 3990    _ = editor.update(cx, |editor, window, cx| {
 3991        editor.change_selections(None, window, cx, |s| {
 3992            s.select_display_ranges([
 3993                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3994                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3995            ])
 3996        });
 3997        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3998        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3999        assert_eq!(
 4000            editor.selections.display_ranges(cx),
 4001            vec![
 4002                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4003                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4004            ]
 4005        );
 4006    });
 4007
 4008    let editor = cx.add_window(|window, cx| {
 4009        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4010        build_editor(buffer, window, cx)
 4011    });
 4012    _ = editor.update(cx, |editor, window, cx| {
 4013        editor.change_selections(None, window, cx, |s| {
 4014            s.select_display_ranges([
 4015                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4016                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4017            ])
 4018        });
 4019        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4020        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4021        assert_eq!(
 4022            editor.selections.display_ranges(cx),
 4023            vec![
 4024                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4025                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4026            ]
 4027        );
 4028    });
 4029}
 4030
 4031#[gpui::test]
 4032fn test_move_line_up_down(cx: &mut TestAppContext) {
 4033    init_test(cx, |_| {});
 4034
 4035    let editor = cx.add_window(|window, cx| {
 4036        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4037        build_editor(buffer, window, cx)
 4038    });
 4039    _ = editor.update(cx, |editor, window, cx| {
 4040        editor.fold_creases(
 4041            vec![
 4042                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4043                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4044                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4045            ],
 4046            true,
 4047            window,
 4048            cx,
 4049        );
 4050        editor.change_selections(None, window, cx, |s| {
 4051            s.select_display_ranges([
 4052                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4053                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4054                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4055                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4056            ])
 4057        });
 4058        assert_eq!(
 4059            editor.display_text(cx),
 4060            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4061        );
 4062
 4063        editor.move_line_up(&MoveLineUp, window, cx);
 4064        assert_eq!(
 4065            editor.display_text(cx),
 4066            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4067        );
 4068        assert_eq!(
 4069            editor.selections.display_ranges(cx),
 4070            vec![
 4071                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4072                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4073                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4074                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4075            ]
 4076        );
 4077    });
 4078
 4079    _ = editor.update(cx, |editor, window, cx| {
 4080        editor.move_line_down(&MoveLineDown, window, cx);
 4081        assert_eq!(
 4082            editor.display_text(cx),
 4083            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4084        );
 4085        assert_eq!(
 4086            editor.selections.display_ranges(cx),
 4087            vec![
 4088                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4089                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4090                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4091                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4092            ]
 4093        );
 4094    });
 4095
 4096    _ = editor.update(cx, |editor, window, cx| {
 4097        editor.move_line_down(&MoveLineDown, window, cx);
 4098        assert_eq!(
 4099            editor.display_text(cx),
 4100            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4101        );
 4102        assert_eq!(
 4103            editor.selections.display_ranges(cx),
 4104            vec![
 4105                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4106                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4107                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4108                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4109            ]
 4110        );
 4111    });
 4112
 4113    _ = editor.update(cx, |editor, window, cx| {
 4114        editor.move_line_up(&MoveLineUp, window, cx);
 4115        assert_eq!(
 4116            editor.display_text(cx),
 4117            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4118        );
 4119        assert_eq!(
 4120            editor.selections.display_ranges(cx),
 4121            vec![
 4122                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4123                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4124                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4125                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4126            ]
 4127        );
 4128    });
 4129}
 4130
 4131#[gpui::test]
 4132fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4133    init_test(cx, |_| {});
 4134
 4135    let editor = cx.add_window(|window, cx| {
 4136        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4137        build_editor(buffer, window, cx)
 4138    });
 4139    _ = editor.update(cx, |editor, window, cx| {
 4140        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4141        editor.insert_blocks(
 4142            [BlockProperties {
 4143                style: BlockStyle::Fixed,
 4144                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4145                height: 1,
 4146                render: Arc::new(|_| div().into_any()),
 4147                priority: 0,
 4148            }],
 4149            Some(Autoscroll::fit()),
 4150            cx,
 4151        );
 4152        editor.change_selections(None, window, cx, |s| {
 4153            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4154        });
 4155        editor.move_line_down(&MoveLineDown, window, cx);
 4156    });
 4157}
 4158
 4159#[gpui::test]
 4160async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4161    init_test(cx, |_| {});
 4162
 4163    let mut cx = EditorTestContext::new(cx).await;
 4164    cx.set_state(
 4165        &"
 4166            ˇzero
 4167            one
 4168            two
 4169            three
 4170            four
 4171            five
 4172        "
 4173        .unindent(),
 4174    );
 4175
 4176    // Create a four-line block that replaces three lines of text.
 4177    cx.update_editor(|editor, window, cx| {
 4178        let snapshot = editor.snapshot(window, cx);
 4179        let snapshot = &snapshot.buffer_snapshot;
 4180        let placement = BlockPlacement::Replace(
 4181            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4182        );
 4183        editor.insert_blocks(
 4184            [BlockProperties {
 4185                placement,
 4186                height: 4,
 4187                style: BlockStyle::Sticky,
 4188                render: Arc::new(|_| gpui::div().into_any_element()),
 4189                priority: 0,
 4190            }],
 4191            None,
 4192            cx,
 4193        );
 4194    });
 4195
 4196    // Move down so that the cursor touches the block.
 4197    cx.update_editor(|editor, window, cx| {
 4198        editor.move_down(&Default::default(), window, cx);
 4199    });
 4200    cx.assert_editor_state(
 4201        &"
 4202            zero
 4203            «one
 4204            two
 4205            threeˇ»
 4206            four
 4207            five
 4208        "
 4209        .unindent(),
 4210    );
 4211
 4212    // Move down past the block.
 4213    cx.update_editor(|editor, window, cx| {
 4214        editor.move_down(&Default::default(), window, cx);
 4215    });
 4216    cx.assert_editor_state(
 4217        &"
 4218            zero
 4219            one
 4220            two
 4221            three
 4222            ˇfour
 4223            five
 4224        "
 4225        .unindent(),
 4226    );
 4227}
 4228
 4229#[gpui::test]
 4230fn test_transpose(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    _ = cx.add_window(|window, cx| {
 4234        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4235        editor.set_style(EditorStyle::default(), window, cx);
 4236        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4237        editor.transpose(&Default::default(), window, cx);
 4238        assert_eq!(editor.text(cx), "bac");
 4239        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4240
 4241        editor.transpose(&Default::default(), window, cx);
 4242        assert_eq!(editor.text(cx), "bca");
 4243        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4244
 4245        editor.transpose(&Default::default(), window, cx);
 4246        assert_eq!(editor.text(cx), "bac");
 4247        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4248
 4249        editor
 4250    });
 4251
 4252    _ = cx.add_window(|window, cx| {
 4253        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4254        editor.set_style(EditorStyle::default(), window, cx);
 4255        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4256        editor.transpose(&Default::default(), window, cx);
 4257        assert_eq!(editor.text(cx), "acb\nde");
 4258        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4259
 4260        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4261        editor.transpose(&Default::default(), window, cx);
 4262        assert_eq!(editor.text(cx), "acbd\ne");
 4263        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4264
 4265        editor.transpose(&Default::default(), window, cx);
 4266        assert_eq!(editor.text(cx), "acbde\n");
 4267        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4268
 4269        editor.transpose(&Default::default(), window, cx);
 4270        assert_eq!(editor.text(cx), "acbd\ne");
 4271        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4272
 4273        editor
 4274    });
 4275
 4276    _ = cx.add_window(|window, cx| {
 4277        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4278        editor.set_style(EditorStyle::default(), window, cx);
 4279        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4280        editor.transpose(&Default::default(), window, cx);
 4281        assert_eq!(editor.text(cx), "bacd\ne");
 4282        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4283
 4284        editor.transpose(&Default::default(), window, cx);
 4285        assert_eq!(editor.text(cx), "bcade\n");
 4286        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4287
 4288        editor.transpose(&Default::default(), window, cx);
 4289        assert_eq!(editor.text(cx), "bcda\ne");
 4290        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4291
 4292        editor.transpose(&Default::default(), window, cx);
 4293        assert_eq!(editor.text(cx), "bcade\n");
 4294        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4295
 4296        editor.transpose(&Default::default(), window, cx);
 4297        assert_eq!(editor.text(cx), "bcaed\n");
 4298        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4299
 4300        editor
 4301    });
 4302
 4303    _ = cx.add_window(|window, cx| {
 4304        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4305        editor.set_style(EditorStyle::default(), window, cx);
 4306        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4307        editor.transpose(&Default::default(), window, cx);
 4308        assert_eq!(editor.text(cx), "🏀🍐✋");
 4309        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4310
 4311        editor.transpose(&Default::default(), window, cx);
 4312        assert_eq!(editor.text(cx), "🏀✋🍐");
 4313        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4314
 4315        editor.transpose(&Default::default(), window, cx);
 4316        assert_eq!(editor.text(cx), "🏀🍐✋");
 4317        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4318
 4319        editor
 4320    });
 4321}
 4322
 4323#[gpui::test]
 4324async fn test_rewrap(cx: &mut TestAppContext) {
 4325    init_test(cx, |_| {});
 4326
 4327    let mut cx = EditorTestContext::new(cx).await;
 4328
 4329    let language_with_c_comments = Arc::new(Language::new(
 4330        LanguageConfig {
 4331            line_comments: vec!["// ".into()],
 4332            ..LanguageConfig::default()
 4333        },
 4334        None,
 4335    ));
 4336    let language_with_pound_comments = Arc::new(Language::new(
 4337        LanguageConfig {
 4338            line_comments: vec!["# ".into()],
 4339            ..LanguageConfig::default()
 4340        },
 4341        None,
 4342    ));
 4343    let markdown_language = Arc::new(Language::new(
 4344        LanguageConfig {
 4345            name: "Markdown".into(),
 4346            ..LanguageConfig::default()
 4347        },
 4348        None,
 4349    ));
 4350    let language_with_doc_comments = Arc::new(Language::new(
 4351        LanguageConfig {
 4352            line_comments: vec!["// ".into(), "/// ".into()],
 4353            ..LanguageConfig::default()
 4354        },
 4355        Some(tree_sitter_rust::LANGUAGE.into()),
 4356    ));
 4357
 4358    let plaintext_language = Arc::new(Language::new(
 4359        LanguageConfig {
 4360            name: "Plain Text".into(),
 4361            ..LanguageConfig::default()
 4362        },
 4363        None,
 4364    ));
 4365
 4366    assert_rewrap(
 4367        indoc! {"
 4368            // ˇ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.
 4369        "},
 4370        indoc! {"
 4371            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4372            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4373            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4374            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4375            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4376            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4377            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4378            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4379            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4380            // porttitor id. Aliquam id accumsan eros.
 4381        "},
 4382        language_with_c_comments.clone(),
 4383        &mut cx,
 4384    );
 4385
 4386    // Test that rewrapping works inside of a selection
 4387    assert_rewrap(
 4388        indoc! {"
 4389            «// 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.ˇ»
 4390        "},
 4391        indoc! {"
 4392            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4393            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4394            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4395            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4396            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4397            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4398            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4399            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4400            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4401            // porttitor id. Aliquam id accumsan eros.ˇ»
 4402        "},
 4403        language_with_c_comments.clone(),
 4404        &mut cx,
 4405    );
 4406
 4407    // Test that cursors that expand to the same region are collapsed.
 4408    assert_rewrap(
 4409        indoc! {"
 4410            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4411            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4412            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4413            // ˇ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.
 4414        "},
 4415        indoc! {"
 4416            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4417            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4418            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4419            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4420            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4421            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4422            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4423            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4424            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4425            // porttitor id. Aliquam id accumsan eros.
 4426        "},
 4427        language_with_c_comments.clone(),
 4428        &mut cx,
 4429    );
 4430
 4431    // Test that non-contiguous selections are treated separately.
 4432    assert_rewrap(
 4433        indoc! {"
 4434            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4435            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4436            //
 4437            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4438            // ˇ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.
 4439        "},
 4440        indoc! {"
 4441            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4442            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4443            // auctor, eu lacinia sapien scelerisque.
 4444            //
 4445            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4446            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4447            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4448            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4449            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4450            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4451            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4452        "},
 4453        language_with_c_comments.clone(),
 4454        &mut cx,
 4455    );
 4456
 4457    // Test that different comment prefixes are supported.
 4458    assert_rewrap(
 4459        indoc! {"
 4460            # ˇ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.
 4461        "},
 4462        indoc! {"
 4463            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4464            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4465            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4466            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4467            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4468            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4469            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4470            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4471            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4472            # accumsan eros.
 4473        "},
 4474        language_with_pound_comments.clone(),
 4475        &mut cx,
 4476    );
 4477
 4478    // Test that rewrapping is ignored outside of comments in most languages.
 4479    assert_rewrap(
 4480        indoc! {"
 4481            /// Adds two numbers.
 4482            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4483            fn add(a: u32, b: u32) -> u32 {
 4484                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ˇ
 4485            }
 4486        "},
 4487        indoc! {"
 4488            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4489            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4490            fn add(a: u32, b: u32) -> u32 {
 4491                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ˇ
 4492            }
 4493        "},
 4494        language_with_doc_comments.clone(),
 4495        &mut cx,
 4496    );
 4497
 4498    // Test that rewrapping works in Markdown and Plain Text languages.
 4499    assert_rewrap(
 4500        indoc! {"
 4501            # Hello
 4502
 4503            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.
 4504        "},
 4505        indoc! {"
 4506            # Hello
 4507
 4508            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4509            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4510            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4511            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4512            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4513            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4514            Integer sit amet scelerisque nisi.
 4515        "},
 4516        markdown_language,
 4517        &mut cx,
 4518    );
 4519
 4520    assert_rewrap(
 4521        indoc! {"
 4522            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.
 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 auctor,
 4527            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4528            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4529            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4530            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4531            Integer sit amet scelerisque nisi.
 4532        "},
 4533        plaintext_language,
 4534        &mut cx,
 4535    );
 4536
 4537    // Test rewrapping unaligned comments in a selection.
 4538    assert_rewrap(
 4539        indoc! {"
 4540            fn foo() {
 4541                if true {
 4542            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4543            // Praesent semper egestas tellus id dignissim.ˇ»
 4544                    do_something();
 4545                } else {
 4546                    //
 4547                }
 4548            }
 4549        "},
 4550        indoc! {"
 4551            fn foo() {
 4552                if true {
 4553            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4554                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4555                    // egestas tellus id dignissim.ˇ»
 4556                    do_something();
 4557                } else {
 4558                    //
 4559                }
 4560            }
 4561        "},
 4562        language_with_doc_comments.clone(),
 4563        &mut cx,
 4564    );
 4565
 4566    assert_rewrap(
 4567        indoc! {"
 4568            fn foo() {
 4569                if true {
 4570            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4571            // Praesent semper egestas tellus id dignissim.»
 4572                    do_something();
 4573                } else {
 4574                    //
 4575                }
 4576
 4577            }
 4578        "},
 4579        indoc! {"
 4580            fn foo() {
 4581                if true {
 4582            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4583                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4584                    // egestas tellus id dignissim.»
 4585                    do_something();
 4586                } else {
 4587                    //
 4588                }
 4589
 4590            }
 4591        "},
 4592        language_with_doc_comments.clone(),
 4593        &mut cx,
 4594    );
 4595
 4596    #[track_caller]
 4597    fn assert_rewrap(
 4598        unwrapped_text: &str,
 4599        wrapped_text: &str,
 4600        language: Arc<Language>,
 4601        cx: &mut EditorTestContext,
 4602    ) {
 4603        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4604        cx.set_state(unwrapped_text);
 4605        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4606        cx.assert_editor_state(wrapped_text);
 4607    }
 4608}
 4609
 4610#[gpui::test]
 4611async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4612    init_test(cx, |_| {});
 4613
 4614    let mut cx = EditorTestContext::new(cx).await;
 4615
 4616    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4617    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4618    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4619
 4620    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4621    cx.set_state("two ˇfour ˇsix ˇ");
 4622    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4623    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4624
 4625    // Paste again but with only two cursors. Since the number of cursors doesn't
 4626    // match the number of slices in the clipboard, the entire clipboard text
 4627    // is pasted at each cursor.
 4628    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4629    cx.update_editor(|e, window, cx| {
 4630        e.handle_input("( ", window, cx);
 4631        e.paste(&Paste, window, cx);
 4632        e.handle_input(") ", window, cx);
 4633    });
 4634    cx.assert_editor_state(
 4635        &([
 4636            "( one✅ ",
 4637            "three ",
 4638            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4639            "three ",
 4640            "five ) ˇ",
 4641        ]
 4642        .join("\n")),
 4643    );
 4644
 4645    // Cut with three selections, one of which is full-line.
 4646    cx.set_state(indoc! {"
 4647        1«2ˇ»3
 4648        4ˇ567
 4649        «8ˇ»9"});
 4650    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4651    cx.assert_editor_state(indoc! {"
 4652        1ˇ3
 4653        ˇ9"});
 4654
 4655    // Paste with three selections, noticing how the copied selection that was full-line
 4656    // gets inserted before the second cursor.
 4657    cx.set_state(indoc! {"
 4658        1ˇ3
 4659 4660        «oˇ»ne"});
 4661    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4662    cx.assert_editor_state(indoc! {"
 4663        12ˇ3
 4664        4567
 4665 4666        8ˇne"});
 4667
 4668    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4669    cx.set_state(indoc! {"
 4670        The quick brown
 4671        fox juˇmps over
 4672        the lazy dog"});
 4673    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4674    assert_eq!(
 4675        cx.read_from_clipboard()
 4676            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4677        Some("fox jumps over\n".to_string())
 4678    );
 4679
 4680    // Paste with three selections, noticing how the copied full-line selection is inserted
 4681    // before the empty selections but replaces the selection that is non-empty.
 4682    cx.set_state(indoc! {"
 4683        Tˇhe quick brown
 4684        «foˇ»x jumps over
 4685        tˇhe lazy dog"});
 4686    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4687    cx.assert_editor_state(indoc! {"
 4688        fox jumps over
 4689        Tˇhe quick brown
 4690        fox jumps over
 4691        ˇx jumps over
 4692        fox jumps over
 4693        tˇhe lazy dog"});
 4694}
 4695
 4696#[gpui::test]
 4697async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4698    init_test(cx, |_| {});
 4699
 4700    let mut cx = EditorTestContext::new(cx).await;
 4701    let language = Arc::new(Language::new(
 4702        LanguageConfig::default(),
 4703        Some(tree_sitter_rust::LANGUAGE.into()),
 4704    ));
 4705    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4706
 4707    // Cut an indented block, without the leading whitespace.
 4708    cx.set_state(indoc! {"
 4709        const a: B = (
 4710            c(),
 4711            «d(
 4712                e,
 4713                f
 4714            )ˇ»
 4715        );
 4716    "});
 4717    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4718    cx.assert_editor_state(indoc! {"
 4719        const a: B = (
 4720            c(),
 4721            ˇ
 4722        );
 4723    "});
 4724
 4725    // Paste it at the same position.
 4726    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4727    cx.assert_editor_state(indoc! {"
 4728        const a: B = (
 4729            c(),
 4730            d(
 4731                e,
 4732                f
 4733 4734        );
 4735    "});
 4736
 4737    // Paste it at a line with a lower indent level.
 4738    cx.set_state(indoc! {"
 4739        ˇ
 4740        const a: B = (
 4741            c(),
 4742        );
 4743    "});
 4744    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4745    cx.assert_editor_state(indoc! {"
 4746        d(
 4747            e,
 4748            f
 4749 4750        const a: B = (
 4751            c(),
 4752        );
 4753    "});
 4754
 4755    // Cut an indented block, with the leading whitespace.
 4756    cx.set_state(indoc! {"
 4757        const a: B = (
 4758            c(),
 4759        «    d(
 4760                e,
 4761                f
 4762            )
 4763        ˇ»);
 4764    "});
 4765    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4766    cx.assert_editor_state(indoc! {"
 4767        const a: B = (
 4768            c(),
 4769        ˇ);
 4770    "});
 4771
 4772    // Paste it at the same position.
 4773    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4774    cx.assert_editor_state(indoc! {"
 4775        const a: B = (
 4776            c(),
 4777            d(
 4778                e,
 4779                f
 4780            )
 4781        ˇ);
 4782    "});
 4783
 4784    // Paste it at a line with a higher indent level.
 4785    cx.set_state(indoc! {"
 4786        const a: B = (
 4787            c(),
 4788            d(
 4789                e,
 4790 4791            )
 4792        );
 4793    "});
 4794    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4795    cx.assert_editor_state(indoc! {"
 4796        const a: B = (
 4797            c(),
 4798            d(
 4799                e,
 4800                f    d(
 4801                    e,
 4802                    f
 4803                )
 4804        ˇ
 4805            )
 4806        );
 4807    "});
 4808}
 4809
 4810#[gpui::test]
 4811fn test_select_all(cx: &mut TestAppContext) {
 4812    init_test(cx, |_| {});
 4813
 4814    let editor = cx.add_window(|window, cx| {
 4815        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4816        build_editor(buffer, window, cx)
 4817    });
 4818    _ = editor.update(cx, |editor, window, cx| {
 4819        editor.select_all(&SelectAll, window, cx);
 4820        assert_eq!(
 4821            editor.selections.display_ranges(cx),
 4822            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4823        );
 4824    });
 4825}
 4826
 4827#[gpui::test]
 4828fn test_select_line(cx: &mut TestAppContext) {
 4829    init_test(cx, |_| {});
 4830
 4831    let editor = cx.add_window(|window, cx| {
 4832        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4833        build_editor(buffer, window, cx)
 4834    });
 4835    _ = editor.update(cx, |editor, window, cx| {
 4836        editor.change_selections(None, window, cx, |s| {
 4837            s.select_display_ranges([
 4838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4839                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4840                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4841                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4842            ])
 4843        });
 4844        editor.select_line(&SelectLine, window, cx);
 4845        assert_eq!(
 4846            editor.selections.display_ranges(cx),
 4847            vec![
 4848                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4849                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4850            ]
 4851        );
 4852    });
 4853
 4854    _ = editor.update(cx, |editor, window, cx| {
 4855        editor.select_line(&SelectLine, window, cx);
 4856        assert_eq!(
 4857            editor.selections.display_ranges(cx),
 4858            vec![
 4859                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4860                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4861            ]
 4862        );
 4863    });
 4864
 4865    _ = editor.update(cx, |editor, window, cx| {
 4866        editor.select_line(&SelectLine, window, cx);
 4867        assert_eq!(
 4868            editor.selections.display_ranges(cx),
 4869            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4870        );
 4871    });
 4872}
 4873
 4874#[gpui::test]
 4875fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4876    init_test(cx, |_| {});
 4877
 4878    let editor = cx.add_window(|window, cx| {
 4879        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4880        build_editor(buffer, window, cx)
 4881    });
 4882    _ = editor.update(cx, |editor, window, cx| {
 4883        editor.fold_creases(
 4884            vec![
 4885                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4886                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4887                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4888            ],
 4889            true,
 4890            window,
 4891            cx,
 4892        );
 4893        editor.change_selections(None, window, cx, |s| {
 4894            s.select_display_ranges([
 4895                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4896                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4897                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4898                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4899            ])
 4900        });
 4901        assert_eq!(
 4902            editor.display_text(cx),
 4903            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4904        );
 4905    });
 4906
 4907    _ = editor.update(cx, |editor, window, cx| {
 4908        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4909        assert_eq!(
 4910            editor.display_text(cx),
 4911            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4912        );
 4913        assert_eq!(
 4914            editor.selections.display_ranges(cx),
 4915            [
 4916                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4917                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4918                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4919                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4920            ]
 4921        );
 4922    });
 4923
 4924    _ = editor.update(cx, |editor, window, cx| {
 4925        editor.change_selections(None, window, cx, |s| {
 4926            s.select_display_ranges([
 4927                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4928            ])
 4929        });
 4930        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4931        assert_eq!(
 4932            editor.display_text(cx),
 4933            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4934        );
 4935        assert_eq!(
 4936            editor.selections.display_ranges(cx),
 4937            [
 4938                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4939                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4940                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4941                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4942                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4943                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4944                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4945                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4946            ]
 4947        );
 4948    });
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let mut cx = EditorTestContext::new(cx).await;
 4956
 4957    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4958    cx.set_state(indoc!(
 4959        r#"abc
 4960           defˇghi
 4961
 4962           jk
 4963           nlmo
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|editor, window, cx| {
 4968        editor.add_selection_above(&Default::default(), window, cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abcˇ
 4973           defˇghi
 4974
 4975           jk
 4976           nlmo
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|editor, window, cx| {
 4981        editor.add_selection_above(&Default::default(), window, cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abcˇ
 4986            defˇghi
 4987
 4988            jk
 4989            nlmo
 4990            "#
 4991    ));
 4992
 4993    cx.update_editor(|editor, window, cx| {
 4994        editor.add_selection_below(&Default::default(), window, cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"abc
 4999           defˇghi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    cx.update_editor(|editor, window, cx| {
 5007        editor.undo_selection(&Default::default(), window, cx);
 5008    });
 5009
 5010    cx.assert_editor_state(indoc!(
 5011        r#"abcˇ
 5012           defˇghi
 5013
 5014           jk
 5015           nlmo
 5016           "#
 5017    ));
 5018
 5019    cx.update_editor(|editor, window, cx| {
 5020        editor.redo_selection(&Default::default(), window, cx);
 5021    });
 5022
 5023    cx.assert_editor_state(indoc!(
 5024        r#"abc
 5025           defˇghi
 5026
 5027           jk
 5028           nlmo
 5029           "#
 5030    ));
 5031
 5032    cx.update_editor(|editor, window, cx| {
 5033        editor.add_selection_below(&Default::default(), window, cx);
 5034    });
 5035
 5036    cx.assert_editor_state(indoc!(
 5037        r#"abc
 5038           defˇghi
 5039
 5040           jk
 5041           nlmˇo
 5042           "#
 5043    ));
 5044
 5045    cx.update_editor(|editor, window, cx| {
 5046        editor.add_selection_below(&Default::default(), window, cx);
 5047    });
 5048
 5049    cx.assert_editor_state(indoc!(
 5050        r#"abc
 5051           defˇghi
 5052
 5053           jk
 5054           nlmˇo
 5055           "#
 5056    ));
 5057
 5058    // change selections
 5059    cx.set_state(indoc!(
 5060        r#"abc
 5061           def«ˇg»hi
 5062
 5063           jk
 5064           nlmo
 5065           "#
 5066    ));
 5067
 5068    cx.update_editor(|editor, window, cx| {
 5069        editor.add_selection_below(&Default::default(), window, cx);
 5070    });
 5071
 5072    cx.assert_editor_state(indoc!(
 5073        r#"abc
 5074           def«ˇg»hi
 5075
 5076           jk
 5077           nlm«ˇo»
 5078           "#
 5079    ));
 5080
 5081    cx.update_editor(|editor, window, cx| {
 5082        editor.add_selection_below(&Default::default(), window, cx);
 5083    });
 5084
 5085    cx.assert_editor_state(indoc!(
 5086        r#"abc
 5087           def«ˇg»hi
 5088
 5089           jk
 5090           nlm«ˇo»
 5091           "#
 5092    ));
 5093
 5094    cx.update_editor(|editor, window, cx| {
 5095        editor.add_selection_above(&Default::default(), window, cx);
 5096    });
 5097
 5098    cx.assert_editor_state(indoc!(
 5099        r#"abc
 5100           def«ˇg»hi
 5101
 5102           jk
 5103           nlmo
 5104           "#
 5105    ));
 5106
 5107    cx.update_editor(|editor, window, cx| {
 5108        editor.add_selection_above(&Default::default(), window, cx);
 5109    });
 5110
 5111    cx.assert_editor_state(indoc!(
 5112        r#"abc
 5113           def«ˇg»hi
 5114
 5115           jk
 5116           nlmo
 5117           "#
 5118    ));
 5119
 5120    // Change selections again
 5121    cx.set_state(indoc!(
 5122        r#"a«bc
 5123           defgˇ»hi
 5124
 5125           jk
 5126           nlmo
 5127           "#
 5128    ));
 5129
 5130    cx.update_editor(|editor, window, cx| {
 5131        editor.add_selection_below(&Default::default(), window, cx);
 5132    });
 5133
 5134    cx.assert_editor_state(indoc!(
 5135        r#"a«bcˇ»
 5136           d«efgˇ»hi
 5137
 5138           j«kˇ»
 5139           nlmo
 5140           "#
 5141    ));
 5142
 5143    cx.update_editor(|editor, window, cx| {
 5144        editor.add_selection_below(&Default::default(), window, cx);
 5145    });
 5146    cx.assert_editor_state(indoc!(
 5147        r#"a«bcˇ»
 5148           d«efgˇ»hi
 5149
 5150           j«kˇ»
 5151           n«lmoˇ»
 5152           "#
 5153    ));
 5154    cx.update_editor(|editor, window, cx| {
 5155        editor.add_selection_above(&Default::default(), window, cx);
 5156    });
 5157
 5158    cx.assert_editor_state(indoc!(
 5159        r#"a«bcˇ»
 5160           d«efgˇ»hi
 5161
 5162           j«kˇ»
 5163           nlmo
 5164           "#
 5165    ));
 5166
 5167    // Change selections again
 5168    cx.set_state(indoc!(
 5169        r#"abc
 5170           d«ˇefghi
 5171
 5172           jk
 5173           nlm»o
 5174           "#
 5175    ));
 5176
 5177    cx.update_editor(|editor, window, cx| {
 5178        editor.add_selection_above(&Default::default(), window, cx);
 5179    });
 5180
 5181    cx.assert_editor_state(indoc!(
 5182        r#"a«ˇbc»
 5183           d«ˇef»ghi
 5184
 5185           j«ˇk»
 5186           n«ˇlm»o
 5187           "#
 5188    ));
 5189
 5190    cx.update_editor(|editor, window, cx| {
 5191        editor.add_selection_below(&Default::default(), window, cx);
 5192    });
 5193
 5194    cx.assert_editor_state(indoc!(
 5195        r#"abc
 5196           d«ˇef»ghi
 5197
 5198           j«ˇk»
 5199           n«ˇlm»o
 5200           "#
 5201    ));
 5202}
 5203
 5204#[gpui::test]
 5205async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5206    init_test(cx, |_| {});
 5207
 5208    let mut cx = EditorTestContext::new(cx).await;
 5209    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5210
 5211    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5212        .unwrap();
 5213    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5214
 5215    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5216        .unwrap();
 5217    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5218
 5219    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5220    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5221
 5222    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5223    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5224
 5225    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5226        .unwrap();
 5227    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5228
 5229    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5230        .unwrap();
 5231    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5232}
 5233
 5234#[gpui::test]
 5235async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5236    init_test(cx, |_| {});
 5237
 5238    let mut cx = EditorTestContext::new(cx).await;
 5239    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5240
 5241    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5242        .unwrap();
 5243    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5244}
 5245
 5246#[gpui::test]
 5247async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5248    init_test(cx, |_| {});
 5249
 5250    let mut cx = EditorTestContext::new(cx).await;
 5251    cx.set_state(
 5252        r#"let foo = 2;
 5253lˇet foo = 2;
 5254let fooˇ = 2;
 5255let foo = 2;
 5256let foo = ˇ2;"#,
 5257    );
 5258
 5259    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5260        .unwrap();
 5261    cx.assert_editor_state(
 5262        r#"let foo = 2;
 5263«letˇ» foo = 2;
 5264let «fooˇ» = 2;
 5265let foo = 2;
 5266let foo = «2ˇ»;"#,
 5267    );
 5268
 5269    // noop for multiple selections with different contents
 5270    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5271        .unwrap();
 5272    cx.assert_editor_state(
 5273        r#"let foo = 2;
 5274«letˇ» foo = 2;
 5275let «fooˇ» = 2;
 5276let foo = 2;
 5277let foo = «2ˇ»;"#,
 5278    );
 5279}
 5280
 5281#[gpui::test]
 5282async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5283    init_test(cx, |_| {});
 5284
 5285    let mut cx =
 5286        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5287
 5288    cx.assert_editor_state(indoc! {"
 5289        ˇbbb
 5290        ccc
 5291
 5292        bbb
 5293        ccc
 5294        "});
 5295    cx.dispatch_action(SelectPrevious::default());
 5296    cx.assert_editor_state(indoc! {"
 5297                «bbbˇ»
 5298                ccc
 5299
 5300                bbb
 5301                ccc
 5302                "});
 5303    cx.dispatch_action(SelectPrevious::default());
 5304    cx.assert_editor_state(indoc! {"
 5305                «bbbˇ»
 5306                ccc
 5307
 5308                «bbbˇ»
 5309                ccc
 5310                "});
 5311}
 5312
 5313#[gpui::test]
 5314async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5315    init_test(cx, |_| {});
 5316
 5317    let mut cx = EditorTestContext::new(cx).await;
 5318    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5319
 5320    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5321        .unwrap();
 5322    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5323
 5324    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5325        .unwrap();
 5326    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5327
 5328    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5329    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5330
 5331    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5332    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5333
 5334    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5335        .unwrap();
 5336    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5337
 5338    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5339        .unwrap();
 5340    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5341
 5342    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5343        .unwrap();
 5344    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5345}
 5346
 5347#[gpui::test]
 5348async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5349    init_test(cx, |_| {});
 5350
 5351    let mut cx = EditorTestContext::new(cx).await;
 5352    cx.set_state(
 5353        r#"let foo = 2;
 5354lˇet foo = 2;
 5355let fooˇ = 2;
 5356let foo = 2;
 5357let foo = ˇ2;"#,
 5358    );
 5359
 5360    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5361        .unwrap();
 5362    cx.assert_editor_state(
 5363        r#"let foo = 2;
 5364«letˇ» foo = 2;
 5365let «fooˇ» = 2;
 5366let foo = 2;
 5367let foo = «2ˇ»;"#,
 5368    );
 5369
 5370    // noop for multiple selections with different contents
 5371    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5372        .unwrap();
 5373    cx.assert_editor_state(
 5374        r#"let foo = 2;
 5375«letˇ» foo = 2;
 5376let «fooˇ» = 2;
 5377let foo = 2;
 5378let foo = «2ˇ»;"#,
 5379    );
 5380}
 5381
 5382#[gpui::test]
 5383async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5384    init_test(cx, |_| {});
 5385
 5386    let mut cx = EditorTestContext::new(cx).await;
 5387    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5388
 5389    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5390        .unwrap();
 5391    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5392
 5393    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5394        .unwrap();
 5395    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5396
 5397    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5398    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5399
 5400    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5401    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5402
 5403    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5404        .unwrap();
 5405    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5406
 5407    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5408        .unwrap();
 5409    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5410}
 5411
 5412#[gpui::test]
 5413async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5414    init_test(cx, |_| {});
 5415
 5416    let language = Arc::new(Language::new(
 5417        LanguageConfig::default(),
 5418        Some(tree_sitter_rust::LANGUAGE.into()),
 5419    ));
 5420
 5421    let text = r#"
 5422        use mod1::mod2::{mod3, mod4};
 5423
 5424        fn fn_1(param1: bool, param2: &str) {
 5425            let var1 = "text";
 5426        }
 5427    "#
 5428    .unindent();
 5429
 5430    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5431    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5432    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5433
 5434    editor
 5435        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5436        .await;
 5437
 5438    editor.update_in(cx, |editor, window, cx| {
 5439        editor.change_selections(None, window, cx, |s| {
 5440            s.select_display_ranges([
 5441                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5442                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5443                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5444            ]);
 5445        });
 5446        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5447    });
 5448    editor.update(cx, |editor, cx| {
 5449        assert_text_with_selections(
 5450            editor,
 5451            indoc! {r#"
 5452                use mod1::mod2::{mod3, «mod4ˇ»};
 5453
 5454                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5455                    let var1 = "«textˇ»";
 5456                }
 5457            "#},
 5458            cx,
 5459        );
 5460    });
 5461
 5462    editor.update_in(cx, |editor, window, cx| {
 5463        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5464    });
 5465    editor.update(cx, |editor, cx| {
 5466        assert_text_with_selections(
 5467            editor,
 5468            indoc! {r#"
 5469                use mod1::mod2::«{mod3, mod4}ˇ»;
 5470
 5471                «ˇfn fn_1(param1: bool, param2: &str) {
 5472                    let var1 = "text";
 5473 5474            "#},
 5475            cx,
 5476        );
 5477    });
 5478
 5479    editor.update_in(cx, |editor, window, cx| {
 5480        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5481    });
 5482    assert_eq!(
 5483        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5484        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5485    );
 5486
 5487    // Trying to expand the selected syntax node one more time has no effect.
 5488    editor.update_in(cx, |editor, window, cx| {
 5489        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5490    });
 5491    assert_eq!(
 5492        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5493        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5494    );
 5495
 5496    editor.update_in(cx, |editor, window, cx| {
 5497        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5498    });
 5499    editor.update(cx, |editor, cx| {
 5500        assert_text_with_selections(
 5501            editor,
 5502            indoc! {r#"
 5503                use mod1::mod2::«{mod3, mod4}ˇ»;
 5504
 5505                «ˇfn fn_1(param1: bool, param2: &str) {
 5506                    let var1 = "text";
 5507 5508            "#},
 5509            cx,
 5510        );
 5511    });
 5512
 5513    editor.update_in(cx, |editor, window, cx| {
 5514        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5515    });
 5516    editor.update(cx, |editor, cx| {
 5517        assert_text_with_selections(
 5518            editor,
 5519            indoc! {r#"
 5520                use mod1::mod2::{mod3, «mod4ˇ»};
 5521
 5522                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5523                    let var1 = "«textˇ»";
 5524                }
 5525            "#},
 5526            cx,
 5527        );
 5528    });
 5529
 5530    editor.update_in(cx, |editor, window, cx| {
 5531        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5532    });
 5533    editor.update(cx, |editor, cx| {
 5534        assert_text_with_selections(
 5535            editor,
 5536            indoc! {r#"
 5537                use mod1::mod2::{mod3, mo«ˇ»d4};
 5538
 5539                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5540                    let var1 = "te«ˇ»xt";
 5541                }
 5542            "#},
 5543            cx,
 5544        );
 5545    });
 5546
 5547    // Trying to shrink the selected syntax node one more time has no effect.
 5548    editor.update_in(cx, |editor, window, cx| {
 5549        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5550    });
 5551    editor.update_in(cx, |editor, _, cx| {
 5552        assert_text_with_selections(
 5553            editor,
 5554            indoc! {r#"
 5555                use mod1::mod2::{mod3, mo«ˇ»d4};
 5556
 5557                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5558                    let var1 = "te«ˇ»xt";
 5559                }
 5560            "#},
 5561            cx,
 5562        );
 5563    });
 5564
 5565    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5566    // a fold.
 5567    editor.update_in(cx, |editor, window, cx| {
 5568        editor.fold_creases(
 5569            vec![
 5570                Crease::simple(
 5571                    Point::new(0, 21)..Point::new(0, 24),
 5572                    FoldPlaceholder::test(),
 5573                ),
 5574                Crease::simple(
 5575                    Point::new(3, 20)..Point::new(3, 22),
 5576                    FoldPlaceholder::test(),
 5577                ),
 5578            ],
 5579            true,
 5580            window,
 5581            cx,
 5582        );
 5583        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5584    });
 5585    editor.update(cx, |editor, cx| {
 5586        assert_text_with_selections(
 5587            editor,
 5588            indoc! {r#"
 5589                use mod1::mod2::«{mod3, mod4}ˇ»;
 5590
 5591                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5592                    «let var1 = "text";ˇ»
 5593                }
 5594            "#},
 5595            cx,
 5596        );
 5597    });
 5598}
 5599
 5600#[gpui::test]
 5601async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5602    init_test(cx, |_| {});
 5603
 5604    let base_text = r#"
 5605        impl A {
 5606            // this is an unstaged comment
 5607
 5608            fn b() {
 5609                c();
 5610            }
 5611
 5612            // this is another unstaged comment
 5613
 5614            fn d() {
 5615                // e
 5616                // f
 5617            }
 5618        }
 5619
 5620        fn g() {
 5621            // h
 5622        }
 5623    "#
 5624    .unindent();
 5625
 5626    let text = r#"
 5627        ˇimpl A {
 5628
 5629            fn b() {
 5630                c();
 5631            }
 5632
 5633            fn d() {
 5634                // e
 5635                // f
 5636            }
 5637        }
 5638
 5639        fn g() {
 5640            // h
 5641        }
 5642    "#
 5643    .unindent();
 5644
 5645    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5646    cx.set_state(&text);
 5647    cx.set_diff_base(&base_text);
 5648    cx.update_editor(|editor, window, cx| {
 5649        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5650    });
 5651
 5652    cx.assert_state_with_diff(
 5653        "
 5654        ˇimpl A {
 5655      -     // this is an unstaged comment
 5656
 5657            fn b() {
 5658                c();
 5659            }
 5660
 5661      -     // this is another unstaged comment
 5662      -
 5663            fn d() {
 5664                // e
 5665                // f
 5666            }
 5667        }
 5668
 5669        fn g() {
 5670            // h
 5671        }
 5672    "
 5673        .unindent(),
 5674    );
 5675
 5676    let expected_display_text = "
 5677        impl A {
 5678            // this is an unstaged comment
 5679
 5680            fn b() {
 5681 5682            }
 5683
 5684            // this is another unstaged comment
 5685
 5686            fn d() {
 5687 5688            }
 5689        }
 5690
 5691        fn g() {
 5692 5693        }
 5694        "
 5695    .unindent();
 5696
 5697    cx.update_editor(|editor, window, cx| {
 5698        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5699        assert_eq!(editor.display_text(cx), expected_display_text);
 5700    });
 5701}
 5702
 5703#[gpui::test]
 5704async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5705    init_test(cx, |_| {});
 5706
 5707    let language = Arc::new(
 5708        Language::new(
 5709            LanguageConfig {
 5710                brackets: BracketPairConfig {
 5711                    pairs: vec![
 5712                        BracketPair {
 5713                            start: "{".to_string(),
 5714                            end: "}".to_string(),
 5715                            close: false,
 5716                            surround: false,
 5717                            newline: true,
 5718                        },
 5719                        BracketPair {
 5720                            start: "(".to_string(),
 5721                            end: ")".to_string(),
 5722                            close: false,
 5723                            surround: false,
 5724                            newline: true,
 5725                        },
 5726                    ],
 5727                    ..Default::default()
 5728                },
 5729                ..Default::default()
 5730            },
 5731            Some(tree_sitter_rust::LANGUAGE.into()),
 5732        )
 5733        .with_indents_query(
 5734            r#"
 5735                (_ "(" ")" @end) @indent
 5736                (_ "{" "}" @end) @indent
 5737            "#,
 5738        )
 5739        .unwrap(),
 5740    );
 5741
 5742    let text = "fn a() {}";
 5743
 5744    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5745    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5746    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5747    editor
 5748        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5749        .await;
 5750
 5751    editor.update_in(cx, |editor, window, cx| {
 5752        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5753        editor.newline(&Newline, window, cx);
 5754        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5755        assert_eq!(
 5756            editor.selections.ranges(cx),
 5757            &[
 5758                Point::new(1, 4)..Point::new(1, 4),
 5759                Point::new(3, 4)..Point::new(3, 4),
 5760                Point::new(5, 0)..Point::new(5, 0)
 5761            ]
 5762        );
 5763    });
 5764}
 5765
 5766#[gpui::test]
 5767async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5768    init_test(cx, |_| {});
 5769
 5770    {
 5771        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5772        cx.set_state(indoc! {"
 5773            impl A {
 5774
 5775                fn b() {}
 5776
 5777            «fn c() {
 5778
 5779            }ˇ»
 5780            }
 5781        "});
 5782
 5783        cx.update_editor(|editor, window, cx| {
 5784            editor.autoindent(&Default::default(), window, cx);
 5785        });
 5786
 5787        cx.assert_editor_state(indoc! {"
 5788            impl A {
 5789
 5790                fn b() {}
 5791
 5792                «fn c() {
 5793
 5794                }ˇ»
 5795            }
 5796        "});
 5797    }
 5798
 5799    {
 5800        let mut cx = EditorTestContext::new_multibuffer(
 5801            cx,
 5802            [indoc! { "
 5803                impl A {
 5804                «
 5805                // a
 5806                fn b(){}
 5807                »
 5808                «
 5809                    }
 5810                    fn c(){}
 5811                »
 5812            "}],
 5813        );
 5814
 5815        let buffer = cx.update_editor(|editor, _, cx| {
 5816            let buffer = editor.buffer().update(cx, |buffer, _| {
 5817                buffer.all_buffers().iter().next().unwrap().clone()
 5818            });
 5819            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5820            buffer
 5821        });
 5822
 5823        cx.run_until_parked();
 5824        cx.update_editor(|editor, window, cx| {
 5825            editor.select_all(&Default::default(), window, cx);
 5826            editor.autoindent(&Default::default(), window, cx)
 5827        });
 5828        cx.run_until_parked();
 5829
 5830        cx.update(|_, cx| {
 5831            pretty_assertions::assert_eq!(
 5832                buffer.read(cx).text(),
 5833                indoc! { "
 5834                    impl A {
 5835
 5836                        // a
 5837                        fn b(){}
 5838
 5839
 5840                    }
 5841                    fn c(){}
 5842
 5843                " }
 5844            )
 5845        });
 5846    }
 5847}
 5848
 5849#[gpui::test]
 5850async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5851    init_test(cx, |_| {});
 5852
 5853    let mut cx = EditorTestContext::new(cx).await;
 5854
 5855    let language = Arc::new(Language::new(
 5856        LanguageConfig {
 5857            brackets: BracketPairConfig {
 5858                pairs: vec![
 5859                    BracketPair {
 5860                        start: "{".to_string(),
 5861                        end: "}".to_string(),
 5862                        close: true,
 5863                        surround: true,
 5864                        newline: true,
 5865                    },
 5866                    BracketPair {
 5867                        start: "(".to_string(),
 5868                        end: ")".to_string(),
 5869                        close: true,
 5870                        surround: true,
 5871                        newline: true,
 5872                    },
 5873                    BracketPair {
 5874                        start: "/*".to_string(),
 5875                        end: " */".to_string(),
 5876                        close: true,
 5877                        surround: true,
 5878                        newline: true,
 5879                    },
 5880                    BracketPair {
 5881                        start: "[".to_string(),
 5882                        end: "]".to_string(),
 5883                        close: false,
 5884                        surround: false,
 5885                        newline: true,
 5886                    },
 5887                    BracketPair {
 5888                        start: "\"".to_string(),
 5889                        end: "\"".to_string(),
 5890                        close: true,
 5891                        surround: true,
 5892                        newline: false,
 5893                    },
 5894                    BracketPair {
 5895                        start: "<".to_string(),
 5896                        end: ">".to_string(),
 5897                        close: false,
 5898                        surround: true,
 5899                        newline: true,
 5900                    },
 5901                ],
 5902                ..Default::default()
 5903            },
 5904            autoclose_before: "})]".to_string(),
 5905            ..Default::default()
 5906        },
 5907        Some(tree_sitter_rust::LANGUAGE.into()),
 5908    ));
 5909
 5910    cx.language_registry().add(language.clone());
 5911    cx.update_buffer(|buffer, cx| {
 5912        buffer.set_language(Some(language), cx);
 5913    });
 5914
 5915    cx.set_state(
 5916        &r#"
 5917            🏀ˇ
 5918            εˇ
 5919            ❤️ˇ
 5920        "#
 5921        .unindent(),
 5922    );
 5923
 5924    // autoclose multiple nested brackets at multiple cursors
 5925    cx.update_editor(|editor, window, cx| {
 5926        editor.handle_input("{", window, cx);
 5927        editor.handle_input("{", window, cx);
 5928        editor.handle_input("{", window, cx);
 5929    });
 5930    cx.assert_editor_state(
 5931        &"
 5932            🏀{{{ˇ}}}
 5933            ε{{{ˇ}}}
 5934            ❤️{{{ˇ}}}
 5935        "
 5936        .unindent(),
 5937    );
 5938
 5939    // insert a different closing bracket
 5940    cx.update_editor(|editor, window, cx| {
 5941        editor.handle_input(")", window, cx);
 5942    });
 5943    cx.assert_editor_state(
 5944        &"
 5945            🏀{{{)ˇ}}}
 5946            ε{{{)ˇ}}}
 5947            ❤️{{{)ˇ}}}
 5948        "
 5949        .unindent(),
 5950    );
 5951
 5952    // skip over the auto-closed brackets when typing a closing bracket
 5953    cx.update_editor(|editor, window, cx| {
 5954        editor.move_right(&MoveRight, window, cx);
 5955        editor.handle_input("}", window, cx);
 5956        editor.handle_input("}", window, cx);
 5957        editor.handle_input("}", window, cx);
 5958    });
 5959    cx.assert_editor_state(
 5960        &"
 5961            🏀{{{)}}}}ˇ
 5962            ε{{{)}}}}ˇ
 5963            ❤️{{{)}}}}ˇ
 5964        "
 5965        .unindent(),
 5966    );
 5967
 5968    // autoclose multi-character pairs
 5969    cx.set_state(
 5970        &"
 5971            ˇ
 5972            ˇ
 5973        "
 5974        .unindent(),
 5975    );
 5976    cx.update_editor(|editor, window, cx| {
 5977        editor.handle_input("/", window, cx);
 5978        editor.handle_input("*", window, cx);
 5979    });
 5980    cx.assert_editor_state(
 5981        &"
 5982            /*ˇ */
 5983            /*ˇ */
 5984        "
 5985        .unindent(),
 5986    );
 5987
 5988    // one cursor autocloses a multi-character pair, one cursor
 5989    // does not autoclose.
 5990    cx.set_state(
 5991        &"
 5992 5993            ˇ
 5994        "
 5995        .unindent(),
 5996    );
 5997    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 5998    cx.assert_editor_state(
 5999        &"
 6000            /*ˇ */
 6001 6002        "
 6003        .unindent(),
 6004    );
 6005
 6006    // Don't autoclose if the next character isn't whitespace and isn't
 6007    // listed in the language's "autoclose_before" section.
 6008    cx.set_state("ˇa b");
 6009    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6010    cx.assert_editor_state("{ˇa b");
 6011
 6012    // Don't autoclose if `close` is false for the bracket pair
 6013    cx.set_state("ˇ");
 6014    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6015    cx.assert_editor_state("");
 6016
 6017    // Surround with brackets if text is selected
 6018    cx.set_state("«aˇ» b");
 6019    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6020    cx.assert_editor_state("{«aˇ»} b");
 6021
 6022    // Autclose pair where the start and end characters are the same
 6023    cx.set_state("");
 6024    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6025    cx.assert_editor_state("a\"ˇ\"");
 6026    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6027    cx.assert_editor_state("a\"\"ˇ");
 6028
 6029    // Don't autoclose pair if autoclose is disabled
 6030    cx.set_state("ˇ");
 6031    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6032    cx.assert_editor_state("");
 6033
 6034    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6035    cx.set_state("«aˇ» b");
 6036    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6037    cx.assert_editor_state("<«aˇ»> b");
 6038}
 6039
 6040#[gpui::test]
 6041async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6042    init_test(cx, |settings| {
 6043        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6044    });
 6045
 6046    let mut cx = EditorTestContext::new(cx).await;
 6047
 6048    let language = Arc::new(Language::new(
 6049        LanguageConfig {
 6050            brackets: BracketPairConfig {
 6051                pairs: vec![
 6052                    BracketPair {
 6053                        start: "{".to_string(),
 6054                        end: "}".to_string(),
 6055                        close: true,
 6056                        surround: true,
 6057                        newline: true,
 6058                    },
 6059                    BracketPair {
 6060                        start: "(".to_string(),
 6061                        end: ")".to_string(),
 6062                        close: true,
 6063                        surround: true,
 6064                        newline: true,
 6065                    },
 6066                    BracketPair {
 6067                        start: "[".to_string(),
 6068                        end: "]".to_string(),
 6069                        close: false,
 6070                        surround: false,
 6071                        newline: true,
 6072                    },
 6073                ],
 6074                ..Default::default()
 6075            },
 6076            autoclose_before: "})]".to_string(),
 6077            ..Default::default()
 6078        },
 6079        Some(tree_sitter_rust::LANGUAGE.into()),
 6080    ));
 6081
 6082    cx.language_registry().add(language.clone());
 6083    cx.update_buffer(|buffer, cx| {
 6084        buffer.set_language(Some(language), cx);
 6085    });
 6086
 6087    cx.set_state(
 6088        &"
 6089            ˇ
 6090            ˇ
 6091            ˇ
 6092        "
 6093        .unindent(),
 6094    );
 6095
 6096    // ensure only matching closing brackets are skipped over
 6097    cx.update_editor(|editor, window, cx| {
 6098        editor.handle_input("}", window, cx);
 6099        editor.move_left(&MoveLeft, window, cx);
 6100        editor.handle_input(")", window, cx);
 6101        editor.move_left(&MoveLeft, window, cx);
 6102    });
 6103    cx.assert_editor_state(
 6104        &"
 6105            ˇ)}
 6106            ˇ)}
 6107            ˇ)}
 6108        "
 6109        .unindent(),
 6110    );
 6111
 6112    // skip-over closing brackets at multiple cursors
 6113    cx.update_editor(|editor, window, cx| {
 6114        editor.handle_input(")", window, cx);
 6115        editor.handle_input("}", window, cx);
 6116    });
 6117    cx.assert_editor_state(
 6118        &"
 6119            )}ˇ
 6120            )}ˇ
 6121            )}ˇ
 6122        "
 6123        .unindent(),
 6124    );
 6125
 6126    // ignore non-close brackets
 6127    cx.update_editor(|editor, window, cx| {
 6128        editor.handle_input("]", window, cx);
 6129        editor.move_left(&MoveLeft, window, cx);
 6130        editor.handle_input("]", window, cx);
 6131    });
 6132    cx.assert_editor_state(
 6133        &"
 6134            )}]ˇ]
 6135            )}]ˇ]
 6136            )}]ˇ]
 6137        "
 6138        .unindent(),
 6139    );
 6140}
 6141
 6142#[gpui::test]
 6143async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6144    init_test(cx, |_| {});
 6145
 6146    let mut cx = EditorTestContext::new(cx).await;
 6147
 6148    let html_language = Arc::new(
 6149        Language::new(
 6150            LanguageConfig {
 6151                name: "HTML".into(),
 6152                brackets: BracketPairConfig {
 6153                    pairs: vec![
 6154                        BracketPair {
 6155                            start: "<".into(),
 6156                            end: ">".into(),
 6157                            close: true,
 6158                            ..Default::default()
 6159                        },
 6160                        BracketPair {
 6161                            start: "{".into(),
 6162                            end: "}".into(),
 6163                            close: true,
 6164                            ..Default::default()
 6165                        },
 6166                        BracketPair {
 6167                            start: "(".into(),
 6168                            end: ")".into(),
 6169                            close: true,
 6170                            ..Default::default()
 6171                        },
 6172                    ],
 6173                    ..Default::default()
 6174                },
 6175                autoclose_before: "})]>".into(),
 6176                ..Default::default()
 6177            },
 6178            Some(tree_sitter_html::language()),
 6179        )
 6180        .with_injection_query(
 6181            r#"
 6182            (script_element
 6183                (raw_text) @injection.content
 6184                (#set! injection.language "javascript"))
 6185            "#,
 6186        )
 6187        .unwrap(),
 6188    );
 6189
 6190    let javascript_language = Arc::new(Language::new(
 6191        LanguageConfig {
 6192            name: "JavaScript".into(),
 6193            brackets: BracketPairConfig {
 6194                pairs: vec![
 6195                    BracketPair {
 6196                        start: "/*".into(),
 6197                        end: " */".into(),
 6198                        close: true,
 6199                        ..Default::default()
 6200                    },
 6201                    BracketPair {
 6202                        start: "{".into(),
 6203                        end: "}".into(),
 6204                        close: true,
 6205                        ..Default::default()
 6206                    },
 6207                    BracketPair {
 6208                        start: "(".into(),
 6209                        end: ")".into(),
 6210                        close: true,
 6211                        ..Default::default()
 6212                    },
 6213                ],
 6214                ..Default::default()
 6215            },
 6216            autoclose_before: "})]>".into(),
 6217            ..Default::default()
 6218        },
 6219        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6220    ));
 6221
 6222    cx.language_registry().add(html_language.clone());
 6223    cx.language_registry().add(javascript_language.clone());
 6224
 6225    cx.update_buffer(|buffer, cx| {
 6226        buffer.set_language(Some(html_language), cx);
 6227    });
 6228
 6229    cx.set_state(
 6230        &r#"
 6231            <body>ˇ
 6232                <script>
 6233                    var x = 1;ˇ
 6234                </script>
 6235            </body>ˇ
 6236        "#
 6237        .unindent(),
 6238    );
 6239
 6240    // Precondition: different languages are active at different locations.
 6241    cx.update_editor(|editor, window, cx| {
 6242        let snapshot = editor.snapshot(window, cx);
 6243        let cursors = editor.selections.ranges::<usize>(cx);
 6244        let languages = cursors
 6245            .iter()
 6246            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6247            .collect::<Vec<_>>();
 6248        assert_eq!(
 6249            languages,
 6250            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6251        );
 6252    });
 6253
 6254    // Angle brackets autoclose in HTML, but not JavaScript.
 6255    cx.update_editor(|editor, window, cx| {
 6256        editor.handle_input("<", window, cx);
 6257        editor.handle_input("a", window, cx);
 6258    });
 6259    cx.assert_editor_state(
 6260        &r#"
 6261            <body><aˇ>
 6262                <script>
 6263                    var x = 1;<aˇ
 6264                </script>
 6265            </body><aˇ>
 6266        "#
 6267        .unindent(),
 6268    );
 6269
 6270    // Curly braces and parens autoclose in both HTML and JavaScript.
 6271    cx.update_editor(|editor, window, cx| {
 6272        editor.handle_input(" b=", window, cx);
 6273        editor.handle_input("{", window, cx);
 6274        editor.handle_input("c", window, cx);
 6275        editor.handle_input("(", window, cx);
 6276    });
 6277    cx.assert_editor_state(
 6278        &r#"
 6279            <body><a b={c(ˇ)}>
 6280                <script>
 6281                    var x = 1;<a b={c(ˇ)}
 6282                </script>
 6283            </body><a b={c(ˇ)}>
 6284        "#
 6285        .unindent(),
 6286    );
 6287
 6288    // Brackets that were already autoclosed are skipped.
 6289    cx.update_editor(|editor, window, cx| {
 6290        editor.handle_input(")", window, cx);
 6291        editor.handle_input("d", window, cx);
 6292        editor.handle_input("}", window, cx);
 6293    });
 6294    cx.assert_editor_state(
 6295        &r#"
 6296            <body><a b={c()d}ˇ>
 6297                <script>
 6298                    var x = 1;<a b={c()d}ˇ
 6299                </script>
 6300            </body><a b={c()d}ˇ>
 6301        "#
 6302        .unindent(),
 6303    );
 6304    cx.update_editor(|editor, window, cx| {
 6305        editor.handle_input(">", window, cx);
 6306    });
 6307    cx.assert_editor_state(
 6308        &r#"
 6309            <body><a b={c()d}>ˇ
 6310                <script>
 6311                    var x = 1;<a b={c()d}>ˇ
 6312                </script>
 6313            </body><a b={c()d}>ˇ
 6314        "#
 6315        .unindent(),
 6316    );
 6317
 6318    // Reset
 6319    cx.set_state(
 6320        &r#"
 6321            <body>ˇ
 6322                <script>
 6323                    var x = 1;ˇ
 6324                </script>
 6325            </body>ˇ
 6326        "#
 6327        .unindent(),
 6328    );
 6329
 6330    cx.update_editor(|editor, window, cx| {
 6331        editor.handle_input("<", window, cx);
 6332    });
 6333    cx.assert_editor_state(
 6334        &r#"
 6335            <body><ˇ>
 6336                <script>
 6337                    var x = 1;<ˇ
 6338                </script>
 6339            </body><ˇ>
 6340        "#
 6341        .unindent(),
 6342    );
 6343
 6344    // When backspacing, the closing angle brackets are removed.
 6345    cx.update_editor(|editor, window, cx| {
 6346        editor.backspace(&Backspace, window, cx);
 6347    });
 6348    cx.assert_editor_state(
 6349        &r#"
 6350            <body>ˇ
 6351                <script>
 6352                    var x = 1;ˇ
 6353                </script>
 6354            </body>ˇ
 6355        "#
 6356        .unindent(),
 6357    );
 6358
 6359    // Block comments autoclose in JavaScript, but not HTML.
 6360    cx.update_editor(|editor, window, cx| {
 6361        editor.handle_input("/", window, cx);
 6362        editor.handle_input("*", window, cx);
 6363    });
 6364    cx.assert_editor_state(
 6365        &r#"
 6366            <body>/*ˇ
 6367                <script>
 6368                    var x = 1;/*ˇ */
 6369                </script>
 6370            </body>/*ˇ
 6371        "#
 6372        .unindent(),
 6373    );
 6374}
 6375
 6376#[gpui::test]
 6377async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6378    init_test(cx, |_| {});
 6379
 6380    let mut cx = EditorTestContext::new(cx).await;
 6381
 6382    let rust_language = Arc::new(
 6383        Language::new(
 6384            LanguageConfig {
 6385                name: "Rust".into(),
 6386                brackets: serde_json::from_value(json!([
 6387                    { "start": "{", "end": "}", "close": true, "newline": true },
 6388                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6389                ]))
 6390                .unwrap(),
 6391                autoclose_before: "})]>".into(),
 6392                ..Default::default()
 6393            },
 6394            Some(tree_sitter_rust::LANGUAGE.into()),
 6395        )
 6396        .with_override_query("(string_literal) @string")
 6397        .unwrap(),
 6398    );
 6399
 6400    cx.language_registry().add(rust_language.clone());
 6401    cx.update_buffer(|buffer, cx| {
 6402        buffer.set_language(Some(rust_language), cx);
 6403    });
 6404
 6405    cx.set_state(
 6406        &r#"
 6407            let x = ˇ
 6408        "#
 6409        .unindent(),
 6410    );
 6411
 6412    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6413    cx.update_editor(|editor, window, cx| {
 6414        editor.handle_input("\"", window, cx);
 6415    });
 6416    cx.assert_editor_state(
 6417        &r#"
 6418            let x = "ˇ"
 6419        "#
 6420        .unindent(),
 6421    );
 6422
 6423    // Inserting another quotation mark. The cursor moves across the existing
 6424    // automatically-inserted quotation mark.
 6425    cx.update_editor(|editor, window, cx| {
 6426        editor.handle_input("\"", window, cx);
 6427    });
 6428    cx.assert_editor_state(
 6429        &r#"
 6430            let x = ""ˇ
 6431        "#
 6432        .unindent(),
 6433    );
 6434
 6435    // Reset
 6436    cx.set_state(
 6437        &r#"
 6438            let x = ˇ
 6439        "#
 6440        .unindent(),
 6441    );
 6442
 6443    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6444    cx.update_editor(|editor, window, cx| {
 6445        editor.handle_input("\"", window, cx);
 6446        editor.handle_input(" ", window, cx);
 6447        editor.move_left(&Default::default(), window, cx);
 6448        editor.handle_input("\\", window, cx);
 6449        editor.handle_input("\"", window, cx);
 6450    });
 6451    cx.assert_editor_state(
 6452        &r#"
 6453            let x = "\"ˇ "
 6454        "#
 6455        .unindent(),
 6456    );
 6457
 6458    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6459    // mark. Nothing is inserted.
 6460    cx.update_editor(|editor, window, cx| {
 6461        editor.move_right(&Default::default(), window, cx);
 6462        editor.handle_input("\"", window, cx);
 6463    });
 6464    cx.assert_editor_state(
 6465        &r#"
 6466            let x = "\" "ˇ
 6467        "#
 6468        .unindent(),
 6469    );
 6470}
 6471
 6472#[gpui::test]
 6473async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6474    init_test(cx, |_| {});
 6475
 6476    let language = Arc::new(Language::new(
 6477        LanguageConfig {
 6478            brackets: BracketPairConfig {
 6479                pairs: vec![
 6480                    BracketPair {
 6481                        start: "{".to_string(),
 6482                        end: "}".to_string(),
 6483                        close: true,
 6484                        surround: true,
 6485                        newline: true,
 6486                    },
 6487                    BracketPair {
 6488                        start: "/* ".to_string(),
 6489                        end: "*/".to_string(),
 6490                        close: true,
 6491                        surround: true,
 6492                        ..Default::default()
 6493                    },
 6494                ],
 6495                ..Default::default()
 6496            },
 6497            ..Default::default()
 6498        },
 6499        Some(tree_sitter_rust::LANGUAGE.into()),
 6500    ));
 6501
 6502    let text = r#"
 6503        a
 6504        b
 6505        c
 6506    "#
 6507    .unindent();
 6508
 6509    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6510    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6511    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6512    editor
 6513        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6514        .await;
 6515
 6516    editor.update_in(cx, |editor, window, cx| {
 6517        editor.change_selections(None, window, cx, |s| {
 6518            s.select_display_ranges([
 6519                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6520                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6521                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6522            ])
 6523        });
 6524
 6525        editor.handle_input("{", window, cx);
 6526        editor.handle_input("{", window, cx);
 6527        editor.handle_input("{", window, cx);
 6528        assert_eq!(
 6529            editor.text(cx),
 6530            "
 6531                {{{a}}}
 6532                {{{b}}}
 6533                {{{c}}}
 6534            "
 6535            .unindent()
 6536        );
 6537        assert_eq!(
 6538            editor.selections.display_ranges(cx),
 6539            [
 6540                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6541                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6542                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6543            ]
 6544        );
 6545
 6546        editor.undo(&Undo, window, cx);
 6547        editor.undo(&Undo, window, cx);
 6548        editor.undo(&Undo, window, cx);
 6549        assert_eq!(
 6550            editor.text(cx),
 6551            "
 6552                a
 6553                b
 6554                c
 6555            "
 6556            .unindent()
 6557        );
 6558        assert_eq!(
 6559            editor.selections.display_ranges(cx),
 6560            [
 6561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6563                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6564            ]
 6565        );
 6566
 6567        // Ensure inserting the first character of a multi-byte bracket pair
 6568        // doesn't surround the selections with the bracket.
 6569        editor.handle_input("/", window, cx);
 6570        assert_eq!(
 6571            editor.text(cx),
 6572            "
 6573                /
 6574                /
 6575                /
 6576            "
 6577            .unindent()
 6578        );
 6579        assert_eq!(
 6580            editor.selections.display_ranges(cx),
 6581            [
 6582                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6583                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6584                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6585            ]
 6586        );
 6587
 6588        editor.undo(&Undo, window, cx);
 6589        assert_eq!(
 6590            editor.text(cx),
 6591            "
 6592                a
 6593                b
 6594                c
 6595            "
 6596            .unindent()
 6597        );
 6598        assert_eq!(
 6599            editor.selections.display_ranges(cx),
 6600            [
 6601                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6602                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6603                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6604            ]
 6605        );
 6606
 6607        // Ensure inserting the last character of a multi-byte bracket pair
 6608        // doesn't surround the selections with the bracket.
 6609        editor.handle_input("*", window, cx);
 6610        assert_eq!(
 6611            editor.text(cx),
 6612            "
 6613                *
 6614                *
 6615                *
 6616            "
 6617            .unindent()
 6618        );
 6619        assert_eq!(
 6620            editor.selections.display_ranges(cx),
 6621            [
 6622                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6623                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6624                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6625            ]
 6626        );
 6627    });
 6628}
 6629
 6630#[gpui::test]
 6631async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6632    init_test(cx, |_| {});
 6633
 6634    let language = Arc::new(Language::new(
 6635        LanguageConfig {
 6636            brackets: BracketPairConfig {
 6637                pairs: vec![BracketPair {
 6638                    start: "{".to_string(),
 6639                    end: "}".to_string(),
 6640                    close: true,
 6641                    surround: true,
 6642                    newline: true,
 6643                }],
 6644                ..Default::default()
 6645            },
 6646            autoclose_before: "}".to_string(),
 6647            ..Default::default()
 6648        },
 6649        Some(tree_sitter_rust::LANGUAGE.into()),
 6650    ));
 6651
 6652    let text = r#"
 6653        a
 6654        b
 6655        c
 6656    "#
 6657    .unindent();
 6658
 6659    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6660    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6661    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6662    editor
 6663        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6664        .await;
 6665
 6666    editor.update_in(cx, |editor, window, cx| {
 6667        editor.change_selections(None, window, cx, |s| {
 6668            s.select_ranges([
 6669                Point::new(0, 1)..Point::new(0, 1),
 6670                Point::new(1, 1)..Point::new(1, 1),
 6671                Point::new(2, 1)..Point::new(2, 1),
 6672            ])
 6673        });
 6674
 6675        editor.handle_input("{", window, cx);
 6676        editor.handle_input("{", window, cx);
 6677        editor.handle_input("_", window, cx);
 6678        assert_eq!(
 6679            editor.text(cx),
 6680            "
 6681                a{{_}}
 6682                b{{_}}
 6683                c{{_}}
 6684            "
 6685            .unindent()
 6686        );
 6687        assert_eq!(
 6688            editor.selections.ranges::<Point>(cx),
 6689            [
 6690                Point::new(0, 4)..Point::new(0, 4),
 6691                Point::new(1, 4)..Point::new(1, 4),
 6692                Point::new(2, 4)..Point::new(2, 4)
 6693            ]
 6694        );
 6695
 6696        editor.backspace(&Default::default(), window, cx);
 6697        editor.backspace(&Default::default(), window, cx);
 6698        assert_eq!(
 6699            editor.text(cx),
 6700            "
 6701                a{}
 6702                b{}
 6703                c{}
 6704            "
 6705            .unindent()
 6706        );
 6707        assert_eq!(
 6708            editor.selections.ranges::<Point>(cx),
 6709            [
 6710                Point::new(0, 2)..Point::new(0, 2),
 6711                Point::new(1, 2)..Point::new(1, 2),
 6712                Point::new(2, 2)..Point::new(2, 2)
 6713            ]
 6714        );
 6715
 6716        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6717        assert_eq!(
 6718            editor.text(cx),
 6719            "
 6720                a
 6721                b
 6722                c
 6723            "
 6724            .unindent()
 6725        );
 6726        assert_eq!(
 6727            editor.selections.ranges::<Point>(cx),
 6728            [
 6729                Point::new(0, 1)..Point::new(0, 1),
 6730                Point::new(1, 1)..Point::new(1, 1),
 6731                Point::new(2, 1)..Point::new(2, 1)
 6732            ]
 6733        );
 6734    });
 6735}
 6736
 6737#[gpui::test]
 6738async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6739    init_test(cx, |settings| {
 6740        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6741    });
 6742
 6743    let mut cx = EditorTestContext::new(cx).await;
 6744
 6745    let language = Arc::new(Language::new(
 6746        LanguageConfig {
 6747            brackets: BracketPairConfig {
 6748                pairs: vec![
 6749                    BracketPair {
 6750                        start: "{".to_string(),
 6751                        end: "}".to_string(),
 6752                        close: true,
 6753                        surround: true,
 6754                        newline: true,
 6755                    },
 6756                    BracketPair {
 6757                        start: "(".to_string(),
 6758                        end: ")".to_string(),
 6759                        close: true,
 6760                        surround: true,
 6761                        newline: true,
 6762                    },
 6763                    BracketPair {
 6764                        start: "[".to_string(),
 6765                        end: "]".to_string(),
 6766                        close: false,
 6767                        surround: true,
 6768                        newline: true,
 6769                    },
 6770                ],
 6771                ..Default::default()
 6772            },
 6773            autoclose_before: "})]".to_string(),
 6774            ..Default::default()
 6775        },
 6776        Some(tree_sitter_rust::LANGUAGE.into()),
 6777    ));
 6778
 6779    cx.language_registry().add(language.clone());
 6780    cx.update_buffer(|buffer, cx| {
 6781        buffer.set_language(Some(language), cx);
 6782    });
 6783
 6784    cx.set_state(
 6785        &"
 6786            {(ˇ)}
 6787            [[ˇ]]
 6788            {(ˇ)}
 6789        "
 6790        .unindent(),
 6791    );
 6792
 6793    cx.update_editor(|editor, window, cx| {
 6794        editor.backspace(&Default::default(), window, cx);
 6795        editor.backspace(&Default::default(), window, cx);
 6796    });
 6797
 6798    cx.assert_editor_state(
 6799        &"
 6800            ˇ
 6801            ˇ]]
 6802            ˇ
 6803        "
 6804        .unindent(),
 6805    );
 6806
 6807    cx.update_editor(|editor, window, cx| {
 6808        editor.handle_input("{", window, cx);
 6809        editor.handle_input("{", window, cx);
 6810        editor.move_right(&MoveRight, window, cx);
 6811        editor.move_right(&MoveRight, window, cx);
 6812        editor.move_left(&MoveLeft, window, cx);
 6813        editor.move_left(&MoveLeft, window, cx);
 6814        editor.backspace(&Default::default(), window, cx);
 6815    });
 6816
 6817    cx.assert_editor_state(
 6818        &"
 6819            {ˇ}
 6820            {ˇ}]]
 6821            {ˇ}
 6822        "
 6823        .unindent(),
 6824    );
 6825
 6826    cx.update_editor(|editor, window, cx| {
 6827        editor.backspace(&Default::default(), window, cx);
 6828    });
 6829
 6830    cx.assert_editor_state(
 6831        &"
 6832            ˇ
 6833            ˇ]]
 6834            ˇ
 6835        "
 6836        .unindent(),
 6837    );
 6838}
 6839
 6840#[gpui::test]
 6841async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6842    init_test(cx, |_| {});
 6843
 6844    let language = Arc::new(Language::new(
 6845        LanguageConfig::default(),
 6846        Some(tree_sitter_rust::LANGUAGE.into()),
 6847    ));
 6848
 6849    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6850    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6851    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6852    editor
 6853        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6854        .await;
 6855
 6856    editor.update_in(cx, |editor, window, cx| {
 6857        editor.set_auto_replace_emoji_shortcode(true);
 6858
 6859        editor.handle_input("Hello ", window, cx);
 6860        editor.handle_input(":wave", window, cx);
 6861        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6862
 6863        editor.handle_input(":", window, cx);
 6864        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6865
 6866        editor.handle_input(" :smile", window, cx);
 6867        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6868
 6869        editor.handle_input(":", window, cx);
 6870        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6871
 6872        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6873        editor.handle_input(":wave", window, cx);
 6874        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6875
 6876        editor.handle_input(":", window, cx);
 6877        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6878
 6879        editor.handle_input(":1", window, cx);
 6880        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6881
 6882        editor.handle_input(":", window, cx);
 6883        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6884
 6885        // Ensure shortcode does not get replaced when it is part of a word
 6886        editor.handle_input(" Test:wave", window, cx);
 6887        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6888
 6889        editor.handle_input(":", window, cx);
 6890        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6891
 6892        editor.set_auto_replace_emoji_shortcode(false);
 6893
 6894        // Ensure shortcode does not get replaced when auto replace is off
 6895        editor.handle_input(" :wave", window, cx);
 6896        assert_eq!(
 6897            editor.text(cx),
 6898            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6899        );
 6900
 6901        editor.handle_input(":", window, cx);
 6902        assert_eq!(
 6903            editor.text(cx),
 6904            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6905        );
 6906    });
 6907}
 6908
 6909#[gpui::test]
 6910async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6911    init_test(cx, |_| {});
 6912
 6913    let (text, insertion_ranges) = marked_text_ranges(
 6914        indoc! {"
 6915            ˇ
 6916        "},
 6917        false,
 6918    );
 6919
 6920    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6921    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6922
 6923    _ = editor.update_in(cx, |editor, window, cx| {
 6924        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6925
 6926        editor
 6927            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6928            .unwrap();
 6929
 6930        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6931            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6932            assert_eq!(editor.text(cx), expected_text);
 6933            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6934        }
 6935
 6936        assert(
 6937            editor,
 6938            cx,
 6939            indoc! {"
 6940            type «» =•
 6941            "},
 6942        );
 6943
 6944        assert!(editor.context_menu_visible(), "There should be a matches");
 6945    });
 6946}
 6947
 6948#[gpui::test]
 6949async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6950    init_test(cx, |_| {});
 6951
 6952    let (text, insertion_ranges) = marked_text_ranges(
 6953        indoc! {"
 6954            a.ˇ b
 6955            a.ˇ b
 6956            a.ˇ b
 6957        "},
 6958        false,
 6959    );
 6960
 6961    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6962    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6963
 6964    editor.update_in(cx, |editor, window, cx| {
 6965        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6966
 6967        editor
 6968            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6969            .unwrap();
 6970
 6971        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6972            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6973            assert_eq!(editor.text(cx), expected_text);
 6974            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6975        }
 6976
 6977        assert(
 6978            editor,
 6979            cx,
 6980            indoc! {"
 6981                a.f(«one», two, «three») b
 6982                a.f(«one», two, «three») b
 6983                a.f(«one», two, «three») b
 6984            "},
 6985        );
 6986
 6987        // Can't move earlier than the first tab stop
 6988        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 6989        assert(
 6990            editor,
 6991            cx,
 6992            indoc! {"
 6993                a.f(«one», two, «three») b
 6994                a.f(«one», two, «three») b
 6995                a.f(«one», two, «three») b
 6996            "},
 6997        );
 6998
 6999        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7000        assert(
 7001            editor,
 7002            cx,
 7003            indoc! {"
 7004                a.f(one, «two», three) b
 7005                a.f(one, «two», three) b
 7006                a.f(one, «two», three) b
 7007            "},
 7008        );
 7009
 7010        editor.move_to_prev_snippet_tabstop(window, cx);
 7011        assert(
 7012            editor,
 7013            cx,
 7014            indoc! {"
 7015                a.f(«one», two, «three») b
 7016                a.f(«one», two, «three») b
 7017                a.f(«one», two, «three») b
 7018            "},
 7019        );
 7020
 7021        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7022        assert(
 7023            editor,
 7024            cx,
 7025            indoc! {"
 7026                a.f(one, «two», three) b
 7027                a.f(one, «two», three) b
 7028                a.f(one, «two», three) b
 7029            "},
 7030        );
 7031        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7032        assert(
 7033            editor,
 7034            cx,
 7035            indoc! {"
 7036                a.f(one, two, three)ˇ b
 7037                a.f(one, two, three)ˇ b
 7038                a.f(one, two, three)ˇ b
 7039            "},
 7040        );
 7041
 7042        // As soon as the last tab stop is reached, snippet state is gone
 7043        editor.move_to_prev_snippet_tabstop(window, cx);
 7044        assert(
 7045            editor,
 7046            cx,
 7047            indoc! {"
 7048                a.f(one, two, three)ˇ b
 7049                a.f(one, two, three)ˇ b
 7050                a.f(one, two, three)ˇ b
 7051            "},
 7052        );
 7053    });
 7054}
 7055
 7056#[gpui::test]
 7057async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7058    init_test(cx, |_| {});
 7059
 7060    let fs = FakeFs::new(cx.executor());
 7061    fs.insert_file("/file.rs", Default::default()).await;
 7062
 7063    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7064
 7065    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7066    language_registry.add(rust_lang());
 7067    let mut fake_servers = language_registry.register_fake_lsp(
 7068        "Rust",
 7069        FakeLspAdapter {
 7070            capabilities: lsp::ServerCapabilities {
 7071                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7072                ..Default::default()
 7073            },
 7074            ..Default::default()
 7075        },
 7076    );
 7077
 7078    let buffer = project
 7079        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7080        .await
 7081        .unwrap();
 7082
 7083    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7084    let (editor, cx) = cx.add_window_view(|window, cx| {
 7085        build_editor_with_project(project.clone(), buffer, window, cx)
 7086    });
 7087    editor.update_in(cx, |editor, window, cx| {
 7088        editor.set_text("one\ntwo\nthree\n", window, cx)
 7089    });
 7090    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7091
 7092    cx.executor().start_waiting();
 7093    let fake_server = fake_servers.next().await.unwrap();
 7094
 7095    let save = editor
 7096        .update_in(cx, |editor, window, cx| {
 7097            editor.save(true, project.clone(), window, cx)
 7098        })
 7099        .unwrap();
 7100    fake_server
 7101        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7102            assert_eq!(
 7103                params.text_document.uri,
 7104                lsp::Url::from_file_path("/file.rs").unwrap()
 7105            );
 7106            assert_eq!(params.options.tab_size, 4);
 7107            Ok(Some(vec![lsp::TextEdit::new(
 7108                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7109                ", ".to_string(),
 7110            )]))
 7111        })
 7112        .next()
 7113        .await;
 7114    cx.executor().start_waiting();
 7115    save.await;
 7116
 7117    assert_eq!(
 7118        editor.update(cx, |editor, cx| editor.text(cx)),
 7119        "one, two\nthree\n"
 7120    );
 7121    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7122
 7123    editor.update_in(cx, |editor, window, cx| {
 7124        editor.set_text("one\ntwo\nthree\n", window, cx)
 7125    });
 7126    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7127
 7128    // Ensure we can still save even if formatting hangs.
 7129    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7130        assert_eq!(
 7131            params.text_document.uri,
 7132            lsp::Url::from_file_path("/file.rs").unwrap()
 7133        );
 7134        futures::future::pending::<()>().await;
 7135        unreachable!()
 7136    });
 7137    let save = editor
 7138        .update_in(cx, |editor, window, cx| {
 7139            editor.save(true, project.clone(), window, cx)
 7140        })
 7141        .unwrap();
 7142    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7143    cx.executor().start_waiting();
 7144    save.await;
 7145    assert_eq!(
 7146        editor.update(cx, |editor, cx| editor.text(cx)),
 7147        "one\ntwo\nthree\n"
 7148    );
 7149    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7150
 7151    // For non-dirty buffer, no formatting request should be sent
 7152    let save = editor
 7153        .update_in(cx, |editor, window, cx| {
 7154            editor.save(true, project.clone(), window, cx)
 7155        })
 7156        .unwrap();
 7157    let _pending_format_request = fake_server
 7158        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7159            panic!("Should not be invoked on non-dirty buffer");
 7160        })
 7161        .next();
 7162    cx.executor().start_waiting();
 7163    save.await;
 7164
 7165    // Set rust language override and assert overridden tabsize is sent to language server
 7166    update_test_language_settings(cx, |settings| {
 7167        settings.languages.insert(
 7168            "Rust".into(),
 7169            LanguageSettingsContent {
 7170                tab_size: NonZeroU32::new(8),
 7171                ..Default::default()
 7172            },
 7173        );
 7174    });
 7175
 7176    editor.update_in(cx, |editor, window, cx| {
 7177        editor.set_text("somehting_new\n", window, cx)
 7178    });
 7179    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7180    let save = editor
 7181        .update_in(cx, |editor, window, cx| {
 7182            editor.save(true, project.clone(), window, cx)
 7183        })
 7184        .unwrap();
 7185    fake_server
 7186        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7187            assert_eq!(
 7188                params.text_document.uri,
 7189                lsp::Url::from_file_path("/file.rs").unwrap()
 7190            );
 7191            assert_eq!(params.options.tab_size, 8);
 7192            Ok(Some(vec![]))
 7193        })
 7194        .next()
 7195        .await;
 7196    cx.executor().start_waiting();
 7197    save.await;
 7198}
 7199
 7200#[gpui::test]
 7201async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7202    init_test(cx, |_| {});
 7203
 7204    let cols = 4;
 7205    let rows = 10;
 7206    let sample_text_1 = sample_text(rows, cols, 'a');
 7207    assert_eq!(
 7208        sample_text_1,
 7209        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7210    );
 7211    let sample_text_2 = sample_text(rows, cols, 'l');
 7212    assert_eq!(
 7213        sample_text_2,
 7214        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7215    );
 7216    let sample_text_3 = sample_text(rows, cols, 'v');
 7217    assert_eq!(
 7218        sample_text_3,
 7219        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7220    );
 7221
 7222    let fs = FakeFs::new(cx.executor());
 7223    fs.insert_tree(
 7224        "/a",
 7225        json!({
 7226            "main.rs": sample_text_1,
 7227            "other.rs": sample_text_2,
 7228            "lib.rs": sample_text_3,
 7229        }),
 7230    )
 7231    .await;
 7232
 7233    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 7234    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7235    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7236
 7237    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7238    language_registry.add(rust_lang());
 7239    let mut fake_servers = language_registry.register_fake_lsp(
 7240        "Rust",
 7241        FakeLspAdapter {
 7242            capabilities: lsp::ServerCapabilities {
 7243                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7244                ..Default::default()
 7245            },
 7246            ..Default::default()
 7247        },
 7248    );
 7249
 7250    let worktree = project.update(cx, |project, cx| {
 7251        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7252        assert_eq!(worktrees.len(), 1);
 7253        worktrees.pop().unwrap()
 7254    });
 7255    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7256
 7257    let buffer_1 = project
 7258        .update(cx, |project, cx| {
 7259            project.open_buffer((worktree_id, "main.rs"), cx)
 7260        })
 7261        .await
 7262        .unwrap();
 7263    let buffer_2 = project
 7264        .update(cx, |project, cx| {
 7265            project.open_buffer((worktree_id, "other.rs"), cx)
 7266        })
 7267        .await
 7268        .unwrap();
 7269    let buffer_3 = project
 7270        .update(cx, |project, cx| {
 7271            project.open_buffer((worktree_id, "lib.rs"), cx)
 7272        })
 7273        .await
 7274        .unwrap();
 7275
 7276    let multi_buffer = cx.new(|cx| {
 7277        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7278        multi_buffer.push_excerpts(
 7279            buffer_1.clone(),
 7280            [
 7281                ExcerptRange {
 7282                    context: Point::new(0, 0)..Point::new(3, 0),
 7283                    primary: None,
 7284                },
 7285                ExcerptRange {
 7286                    context: Point::new(5, 0)..Point::new(7, 0),
 7287                    primary: None,
 7288                },
 7289                ExcerptRange {
 7290                    context: Point::new(9, 0)..Point::new(10, 4),
 7291                    primary: None,
 7292                },
 7293            ],
 7294            cx,
 7295        );
 7296        multi_buffer.push_excerpts(
 7297            buffer_2.clone(),
 7298            [
 7299                ExcerptRange {
 7300                    context: Point::new(0, 0)..Point::new(3, 0),
 7301                    primary: None,
 7302                },
 7303                ExcerptRange {
 7304                    context: Point::new(5, 0)..Point::new(7, 0),
 7305                    primary: None,
 7306                },
 7307                ExcerptRange {
 7308                    context: Point::new(9, 0)..Point::new(10, 4),
 7309                    primary: None,
 7310                },
 7311            ],
 7312            cx,
 7313        );
 7314        multi_buffer.push_excerpts(
 7315            buffer_3.clone(),
 7316            [
 7317                ExcerptRange {
 7318                    context: Point::new(0, 0)..Point::new(3, 0),
 7319                    primary: None,
 7320                },
 7321                ExcerptRange {
 7322                    context: Point::new(5, 0)..Point::new(7, 0),
 7323                    primary: None,
 7324                },
 7325                ExcerptRange {
 7326                    context: Point::new(9, 0)..Point::new(10, 4),
 7327                    primary: None,
 7328                },
 7329            ],
 7330            cx,
 7331        );
 7332        multi_buffer
 7333    });
 7334    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7335        Editor::new(
 7336            EditorMode::Full,
 7337            multi_buffer,
 7338            Some(project.clone()),
 7339            true,
 7340            window,
 7341            cx,
 7342        )
 7343    });
 7344
 7345    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7346        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7347            s.select_ranges(Some(1..2))
 7348        });
 7349        editor.insert("|one|two|three|", window, cx);
 7350    });
 7351    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7352    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7353        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7354            s.select_ranges(Some(60..70))
 7355        });
 7356        editor.insert("|four|five|six|", window, cx);
 7357    });
 7358    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7359
 7360    // First two buffers should be edited, but not the third one.
 7361    assert_eq!(
 7362        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7363        "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}",
 7364    );
 7365    buffer_1.update(cx, |buffer, _| {
 7366        assert!(buffer.is_dirty());
 7367        assert_eq!(
 7368            buffer.text(),
 7369            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7370        )
 7371    });
 7372    buffer_2.update(cx, |buffer, _| {
 7373        assert!(buffer.is_dirty());
 7374        assert_eq!(
 7375            buffer.text(),
 7376            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7377        )
 7378    });
 7379    buffer_3.update(cx, |buffer, _| {
 7380        assert!(!buffer.is_dirty());
 7381        assert_eq!(buffer.text(), sample_text_3,)
 7382    });
 7383    cx.executor().run_until_parked();
 7384
 7385    cx.executor().start_waiting();
 7386    let save = multi_buffer_editor
 7387        .update_in(cx, |editor, window, cx| {
 7388            editor.save(true, project.clone(), window, cx)
 7389        })
 7390        .unwrap();
 7391
 7392    let fake_server = fake_servers.next().await.unwrap();
 7393    fake_server
 7394        .server
 7395        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7396            Ok(Some(vec![lsp::TextEdit::new(
 7397                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7398                format!("[{} formatted]", params.text_document.uri),
 7399            )]))
 7400        })
 7401        .detach();
 7402    save.await;
 7403
 7404    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7405    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7406    assert_eq!(
 7407        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7408        "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}",
 7409    );
 7410    buffer_1.update(cx, |buffer, _| {
 7411        assert!(!buffer.is_dirty());
 7412        assert_eq!(
 7413            buffer.text(),
 7414            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7415        )
 7416    });
 7417    buffer_2.update(cx, |buffer, _| {
 7418        assert!(!buffer.is_dirty());
 7419        assert_eq!(
 7420            buffer.text(),
 7421            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7422        )
 7423    });
 7424    buffer_3.update(cx, |buffer, _| {
 7425        assert!(!buffer.is_dirty());
 7426        assert_eq!(buffer.text(), sample_text_3,)
 7427    });
 7428}
 7429
 7430#[gpui::test]
 7431async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7432    init_test(cx, |_| {});
 7433
 7434    let fs = FakeFs::new(cx.executor());
 7435    fs.insert_file("/file.rs", Default::default()).await;
 7436
 7437    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7438
 7439    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7440    language_registry.add(rust_lang());
 7441    let mut fake_servers = language_registry.register_fake_lsp(
 7442        "Rust",
 7443        FakeLspAdapter {
 7444            capabilities: lsp::ServerCapabilities {
 7445                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7446                ..Default::default()
 7447            },
 7448            ..Default::default()
 7449        },
 7450    );
 7451
 7452    let buffer = project
 7453        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7454        .await
 7455        .unwrap();
 7456
 7457    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7458    let (editor, cx) = cx.add_window_view(|window, cx| {
 7459        build_editor_with_project(project.clone(), buffer, window, cx)
 7460    });
 7461    editor.update_in(cx, |editor, window, cx| {
 7462        editor.set_text("one\ntwo\nthree\n", window, cx)
 7463    });
 7464    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7465
 7466    cx.executor().start_waiting();
 7467    let fake_server = fake_servers.next().await.unwrap();
 7468
 7469    let save = editor
 7470        .update_in(cx, |editor, window, cx| {
 7471            editor.save(true, project.clone(), window, cx)
 7472        })
 7473        .unwrap();
 7474    fake_server
 7475        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7476            assert_eq!(
 7477                params.text_document.uri,
 7478                lsp::Url::from_file_path("/file.rs").unwrap()
 7479            );
 7480            assert_eq!(params.options.tab_size, 4);
 7481            Ok(Some(vec![lsp::TextEdit::new(
 7482                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7483                ", ".to_string(),
 7484            )]))
 7485        })
 7486        .next()
 7487        .await;
 7488    cx.executor().start_waiting();
 7489    save.await;
 7490    assert_eq!(
 7491        editor.update(cx, |editor, cx| editor.text(cx)),
 7492        "one, two\nthree\n"
 7493    );
 7494    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7495
 7496    editor.update_in(cx, |editor, window, cx| {
 7497        editor.set_text("one\ntwo\nthree\n", window, cx)
 7498    });
 7499    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7500
 7501    // Ensure we can still save even if formatting hangs.
 7502    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7503        move |params, _| async move {
 7504            assert_eq!(
 7505                params.text_document.uri,
 7506                lsp::Url::from_file_path("/file.rs").unwrap()
 7507            );
 7508            futures::future::pending::<()>().await;
 7509            unreachable!()
 7510        },
 7511    );
 7512    let save = editor
 7513        .update_in(cx, |editor, window, cx| {
 7514            editor.save(true, project.clone(), window, cx)
 7515        })
 7516        .unwrap();
 7517    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7518    cx.executor().start_waiting();
 7519    save.await;
 7520    assert_eq!(
 7521        editor.update(cx, |editor, cx| editor.text(cx)),
 7522        "one\ntwo\nthree\n"
 7523    );
 7524    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7525
 7526    // For non-dirty buffer, no formatting request should be sent
 7527    let save = editor
 7528        .update_in(cx, |editor, window, cx| {
 7529            editor.save(true, project.clone(), window, cx)
 7530        })
 7531        .unwrap();
 7532    let _pending_format_request = fake_server
 7533        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7534            panic!("Should not be invoked on non-dirty buffer");
 7535        })
 7536        .next();
 7537    cx.executor().start_waiting();
 7538    save.await;
 7539
 7540    // Set Rust language override and assert overridden tabsize is sent to language server
 7541    update_test_language_settings(cx, |settings| {
 7542        settings.languages.insert(
 7543            "Rust".into(),
 7544            LanguageSettingsContent {
 7545                tab_size: NonZeroU32::new(8),
 7546                ..Default::default()
 7547            },
 7548        );
 7549    });
 7550
 7551    editor.update_in(cx, |editor, window, cx| {
 7552        editor.set_text("somehting_new\n", window, cx)
 7553    });
 7554    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7555    let save = editor
 7556        .update_in(cx, |editor, window, cx| {
 7557            editor.save(true, project.clone(), window, cx)
 7558        })
 7559        .unwrap();
 7560    fake_server
 7561        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7562            assert_eq!(
 7563                params.text_document.uri,
 7564                lsp::Url::from_file_path("/file.rs").unwrap()
 7565            );
 7566            assert_eq!(params.options.tab_size, 8);
 7567            Ok(Some(vec![]))
 7568        })
 7569        .next()
 7570        .await;
 7571    cx.executor().start_waiting();
 7572    save.await;
 7573}
 7574
 7575#[gpui::test]
 7576async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7577    init_test(cx, |settings| {
 7578        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7579            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7580        ))
 7581    });
 7582
 7583    let fs = FakeFs::new(cx.executor());
 7584    fs.insert_file("/file.rs", Default::default()).await;
 7585
 7586    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7587
 7588    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7589    language_registry.add(Arc::new(Language::new(
 7590        LanguageConfig {
 7591            name: "Rust".into(),
 7592            matcher: LanguageMatcher {
 7593                path_suffixes: vec!["rs".to_string()],
 7594                ..Default::default()
 7595            },
 7596            ..LanguageConfig::default()
 7597        },
 7598        Some(tree_sitter_rust::LANGUAGE.into()),
 7599    )));
 7600    update_test_language_settings(cx, |settings| {
 7601        // Enable Prettier formatting for the same buffer, and ensure
 7602        // LSP is called instead of Prettier.
 7603        settings.defaults.prettier = Some(PrettierSettings {
 7604            allowed: true,
 7605            ..PrettierSettings::default()
 7606        });
 7607    });
 7608    let mut fake_servers = language_registry.register_fake_lsp(
 7609        "Rust",
 7610        FakeLspAdapter {
 7611            capabilities: lsp::ServerCapabilities {
 7612                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7613                ..Default::default()
 7614            },
 7615            ..Default::default()
 7616        },
 7617    );
 7618
 7619    let buffer = project
 7620        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7621        .await
 7622        .unwrap();
 7623
 7624    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7625    let (editor, cx) = cx.add_window_view(|window, cx| {
 7626        build_editor_with_project(project.clone(), buffer, window, cx)
 7627    });
 7628    editor.update_in(cx, |editor, window, cx| {
 7629        editor.set_text("one\ntwo\nthree\n", window, cx)
 7630    });
 7631
 7632    cx.executor().start_waiting();
 7633    let fake_server = fake_servers.next().await.unwrap();
 7634
 7635    let format = editor
 7636        .update_in(cx, |editor, window, cx| {
 7637            editor.perform_format(
 7638                project.clone(),
 7639                FormatTrigger::Manual,
 7640                FormatTarget::Buffers,
 7641                window,
 7642                cx,
 7643            )
 7644        })
 7645        .unwrap();
 7646    fake_server
 7647        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7648            assert_eq!(
 7649                params.text_document.uri,
 7650                lsp::Url::from_file_path("/file.rs").unwrap()
 7651            );
 7652            assert_eq!(params.options.tab_size, 4);
 7653            Ok(Some(vec![lsp::TextEdit::new(
 7654                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7655                ", ".to_string(),
 7656            )]))
 7657        })
 7658        .next()
 7659        .await;
 7660    cx.executor().start_waiting();
 7661    format.await;
 7662    assert_eq!(
 7663        editor.update(cx, |editor, cx| editor.text(cx)),
 7664        "one, two\nthree\n"
 7665    );
 7666
 7667    editor.update_in(cx, |editor, window, cx| {
 7668        editor.set_text("one\ntwo\nthree\n", window, cx)
 7669    });
 7670    // Ensure we don't lock if formatting hangs.
 7671    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7672        assert_eq!(
 7673            params.text_document.uri,
 7674            lsp::Url::from_file_path("/file.rs").unwrap()
 7675        );
 7676        futures::future::pending::<()>().await;
 7677        unreachable!()
 7678    });
 7679    let format = editor
 7680        .update_in(cx, |editor, window, cx| {
 7681            editor.perform_format(
 7682                project,
 7683                FormatTrigger::Manual,
 7684                FormatTarget::Buffers,
 7685                window,
 7686                cx,
 7687            )
 7688        })
 7689        .unwrap();
 7690    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7691    cx.executor().start_waiting();
 7692    format.await;
 7693    assert_eq!(
 7694        editor.update(cx, |editor, cx| editor.text(cx)),
 7695        "one\ntwo\nthree\n"
 7696    );
 7697}
 7698
 7699#[gpui::test]
 7700async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7701    init_test(cx, |_| {});
 7702
 7703    let mut cx = EditorLspTestContext::new_rust(
 7704        lsp::ServerCapabilities {
 7705            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7706            ..Default::default()
 7707        },
 7708        cx,
 7709    )
 7710    .await;
 7711
 7712    cx.set_state(indoc! {"
 7713        one.twoˇ
 7714    "});
 7715
 7716    // The format request takes a long time. When it completes, it inserts
 7717    // a newline and an indent before the `.`
 7718    cx.lsp
 7719        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7720            let executor = cx.background_executor().clone();
 7721            async move {
 7722                executor.timer(Duration::from_millis(100)).await;
 7723                Ok(Some(vec![lsp::TextEdit {
 7724                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7725                    new_text: "\n    ".into(),
 7726                }]))
 7727            }
 7728        });
 7729
 7730    // Submit a format request.
 7731    let format_1 = cx
 7732        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7733        .unwrap();
 7734    cx.executor().run_until_parked();
 7735
 7736    // Submit a second format request.
 7737    let format_2 = cx
 7738        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7739        .unwrap();
 7740    cx.executor().run_until_parked();
 7741
 7742    // Wait for both format requests to complete
 7743    cx.executor().advance_clock(Duration::from_millis(200));
 7744    cx.executor().start_waiting();
 7745    format_1.await.unwrap();
 7746    cx.executor().start_waiting();
 7747    format_2.await.unwrap();
 7748
 7749    // The formatting edits only happens once.
 7750    cx.assert_editor_state(indoc! {"
 7751        one
 7752            .twoˇ
 7753    "});
 7754}
 7755
 7756#[gpui::test]
 7757async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7758    init_test(cx, |settings| {
 7759        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7760    });
 7761
 7762    let mut cx = EditorLspTestContext::new_rust(
 7763        lsp::ServerCapabilities {
 7764            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7765            ..Default::default()
 7766        },
 7767        cx,
 7768    )
 7769    .await;
 7770
 7771    // Set up a buffer white some trailing whitespace and no trailing newline.
 7772    cx.set_state(
 7773        &[
 7774            "one ",   //
 7775            "twoˇ",   //
 7776            "three ", //
 7777            "four",   //
 7778        ]
 7779        .join("\n"),
 7780    );
 7781
 7782    // Submit a format request.
 7783    let format = cx
 7784        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7785        .unwrap();
 7786
 7787    // Record which buffer changes have been sent to the language server
 7788    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7789    cx.lsp
 7790        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7791            let buffer_changes = buffer_changes.clone();
 7792            move |params, _| {
 7793                buffer_changes.lock().extend(
 7794                    params
 7795                        .content_changes
 7796                        .into_iter()
 7797                        .map(|e| (e.range.unwrap(), e.text)),
 7798                );
 7799            }
 7800        });
 7801
 7802    // Handle formatting requests to the language server.
 7803    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7804        let buffer_changes = buffer_changes.clone();
 7805        move |_, _| {
 7806            // When formatting is requested, trailing whitespace has already been stripped,
 7807            // and the trailing newline has already been added.
 7808            assert_eq!(
 7809                &buffer_changes.lock()[1..],
 7810                &[
 7811                    (
 7812                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7813                        "".into()
 7814                    ),
 7815                    (
 7816                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7817                        "".into()
 7818                    ),
 7819                    (
 7820                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7821                        "\n".into()
 7822                    ),
 7823                ]
 7824            );
 7825
 7826            // Insert blank lines between each line of the buffer.
 7827            async move {
 7828                Ok(Some(vec![
 7829                    lsp::TextEdit {
 7830                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7831                        new_text: "\n".into(),
 7832                    },
 7833                    lsp::TextEdit {
 7834                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7835                        new_text: "\n".into(),
 7836                    },
 7837                ]))
 7838            }
 7839        }
 7840    });
 7841
 7842    // After formatting the buffer, the trailing whitespace is stripped,
 7843    // a newline is appended, and the edits provided by the language server
 7844    // have been applied.
 7845    format.await.unwrap();
 7846    cx.assert_editor_state(
 7847        &[
 7848            "one",   //
 7849            "",      //
 7850            "twoˇ",  //
 7851            "",      //
 7852            "three", //
 7853            "four",  //
 7854            "",      //
 7855        ]
 7856        .join("\n"),
 7857    );
 7858
 7859    // Undoing the formatting undoes the trailing whitespace removal, the
 7860    // trailing newline, and the LSP edits.
 7861    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7862    cx.assert_editor_state(
 7863        &[
 7864            "one ",   //
 7865            "twoˇ",   //
 7866            "three ", //
 7867            "four",   //
 7868        ]
 7869        .join("\n"),
 7870    );
 7871}
 7872
 7873#[gpui::test]
 7874async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7875    cx: &mut gpui::TestAppContext,
 7876) {
 7877    init_test(cx, |_| {});
 7878
 7879    cx.update(|cx| {
 7880        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7881            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7882                settings.auto_signature_help = Some(true);
 7883            });
 7884        });
 7885    });
 7886
 7887    let mut cx = EditorLspTestContext::new_rust(
 7888        lsp::ServerCapabilities {
 7889            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7890                ..Default::default()
 7891            }),
 7892            ..Default::default()
 7893        },
 7894        cx,
 7895    )
 7896    .await;
 7897
 7898    let language = Language::new(
 7899        LanguageConfig {
 7900            name: "Rust".into(),
 7901            brackets: BracketPairConfig {
 7902                pairs: vec![
 7903                    BracketPair {
 7904                        start: "{".to_string(),
 7905                        end: "}".to_string(),
 7906                        close: true,
 7907                        surround: true,
 7908                        newline: true,
 7909                    },
 7910                    BracketPair {
 7911                        start: "(".to_string(),
 7912                        end: ")".to_string(),
 7913                        close: true,
 7914                        surround: true,
 7915                        newline: true,
 7916                    },
 7917                    BracketPair {
 7918                        start: "/*".to_string(),
 7919                        end: " */".to_string(),
 7920                        close: true,
 7921                        surround: true,
 7922                        newline: true,
 7923                    },
 7924                    BracketPair {
 7925                        start: "[".to_string(),
 7926                        end: "]".to_string(),
 7927                        close: false,
 7928                        surround: false,
 7929                        newline: true,
 7930                    },
 7931                    BracketPair {
 7932                        start: "\"".to_string(),
 7933                        end: "\"".to_string(),
 7934                        close: true,
 7935                        surround: true,
 7936                        newline: false,
 7937                    },
 7938                    BracketPair {
 7939                        start: "<".to_string(),
 7940                        end: ">".to_string(),
 7941                        close: false,
 7942                        surround: true,
 7943                        newline: true,
 7944                    },
 7945                ],
 7946                ..Default::default()
 7947            },
 7948            autoclose_before: "})]".to_string(),
 7949            ..Default::default()
 7950        },
 7951        Some(tree_sitter_rust::LANGUAGE.into()),
 7952    );
 7953    let language = Arc::new(language);
 7954
 7955    cx.language_registry().add(language.clone());
 7956    cx.update_buffer(|buffer, cx| {
 7957        buffer.set_language(Some(language), cx);
 7958    });
 7959
 7960    cx.set_state(
 7961        &r#"
 7962            fn main() {
 7963                sampleˇ
 7964            }
 7965        "#
 7966        .unindent(),
 7967    );
 7968
 7969    cx.update_editor(|editor, window, cx| {
 7970        editor.handle_input("(", window, cx);
 7971    });
 7972    cx.assert_editor_state(
 7973        &"
 7974            fn main() {
 7975                sample(ˇ)
 7976            }
 7977        "
 7978        .unindent(),
 7979    );
 7980
 7981    let mocked_response = lsp::SignatureHelp {
 7982        signatures: vec![lsp::SignatureInformation {
 7983            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7984            documentation: None,
 7985            parameters: Some(vec![
 7986                lsp::ParameterInformation {
 7987                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7988                    documentation: None,
 7989                },
 7990                lsp::ParameterInformation {
 7991                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7992                    documentation: None,
 7993                },
 7994            ]),
 7995            active_parameter: None,
 7996        }],
 7997        active_signature: Some(0),
 7998        active_parameter: Some(0),
 7999    };
 8000    handle_signature_help_request(&mut cx, mocked_response).await;
 8001
 8002    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8003        .await;
 8004
 8005    cx.editor(|editor, _, _| {
 8006        let signature_help_state = editor.signature_help_state.popover().cloned();
 8007        assert!(signature_help_state.is_some());
 8008        let ParsedMarkdown {
 8009            text, highlights, ..
 8010        } = signature_help_state.unwrap().parsed_content;
 8011        assert_eq!(text, "param1: u8, param2: u8");
 8012        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8013    });
 8014}
 8015
 8016#[gpui::test]
 8017async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8018    init_test(cx, |_| {});
 8019
 8020    cx.update(|cx| {
 8021        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8022            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8023                settings.auto_signature_help = Some(false);
 8024                settings.show_signature_help_after_edits = Some(false);
 8025            });
 8026        });
 8027    });
 8028
 8029    let mut cx = EditorLspTestContext::new_rust(
 8030        lsp::ServerCapabilities {
 8031            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8032                ..Default::default()
 8033            }),
 8034            ..Default::default()
 8035        },
 8036        cx,
 8037    )
 8038    .await;
 8039
 8040    let language = Language::new(
 8041        LanguageConfig {
 8042            name: "Rust".into(),
 8043            brackets: BracketPairConfig {
 8044                pairs: vec![
 8045                    BracketPair {
 8046                        start: "{".to_string(),
 8047                        end: "}".to_string(),
 8048                        close: true,
 8049                        surround: true,
 8050                        newline: true,
 8051                    },
 8052                    BracketPair {
 8053                        start: "(".to_string(),
 8054                        end: ")".to_string(),
 8055                        close: true,
 8056                        surround: true,
 8057                        newline: true,
 8058                    },
 8059                    BracketPair {
 8060                        start: "/*".to_string(),
 8061                        end: " */".to_string(),
 8062                        close: true,
 8063                        surround: true,
 8064                        newline: true,
 8065                    },
 8066                    BracketPair {
 8067                        start: "[".to_string(),
 8068                        end: "]".to_string(),
 8069                        close: false,
 8070                        surround: false,
 8071                        newline: true,
 8072                    },
 8073                    BracketPair {
 8074                        start: "\"".to_string(),
 8075                        end: "\"".to_string(),
 8076                        close: true,
 8077                        surround: true,
 8078                        newline: false,
 8079                    },
 8080                    BracketPair {
 8081                        start: "<".to_string(),
 8082                        end: ">".to_string(),
 8083                        close: false,
 8084                        surround: true,
 8085                        newline: true,
 8086                    },
 8087                ],
 8088                ..Default::default()
 8089            },
 8090            autoclose_before: "})]".to_string(),
 8091            ..Default::default()
 8092        },
 8093        Some(tree_sitter_rust::LANGUAGE.into()),
 8094    );
 8095    let language = Arc::new(language);
 8096
 8097    cx.language_registry().add(language.clone());
 8098    cx.update_buffer(|buffer, cx| {
 8099        buffer.set_language(Some(language), cx);
 8100    });
 8101
 8102    // Ensure that signature_help is not called when no signature help is enabled.
 8103    cx.set_state(
 8104        &r#"
 8105            fn main() {
 8106                sampleˇ
 8107            }
 8108        "#
 8109        .unindent(),
 8110    );
 8111    cx.update_editor(|editor, window, cx| {
 8112        editor.handle_input("(", window, cx);
 8113    });
 8114    cx.assert_editor_state(
 8115        &"
 8116            fn main() {
 8117                sample(ˇ)
 8118            }
 8119        "
 8120        .unindent(),
 8121    );
 8122    cx.editor(|editor, _, _| {
 8123        assert!(editor.signature_help_state.task().is_none());
 8124    });
 8125
 8126    let mocked_response = lsp::SignatureHelp {
 8127        signatures: vec![lsp::SignatureInformation {
 8128            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8129            documentation: None,
 8130            parameters: Some(vec![
 8131                lsp::ParameterInformation {
 8132                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8133                    documentation: None,
 8134                },
 8135                lsp::ParameterInformation {
 8136                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8137                    documentation: None,
 8138                },
 8139            ]),
 8140            active_parameter: None,
 8141        }],
 8142        active_signature: Some(0),
 8143        active_parameter: Some(0),
 8144    };
 8145
 8146    // Ensure that signature_help is called when enabled afte edits
 8147    cx.update(|_, cx| {
 8148        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8149            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8150                settings.auto_signature_help = Some(false);
 8151                settings.show_signature_help_after_edits = Some(true);
 8152            });
 8153        });
 8154    });
 8155    cx.set_state(
 8156        &r#"
 8157            fn main() {
 8158                sampleˇ
 8159            }
 8160        "#
 8161        .unindent(),
 8162    );
 8163    cx.update_editor(|editor, window, cx| {
 8164        editor.handle_input("(", window, cx);
 8165    });
 8166    cx.assert_editor_state(
 8167        &"
 8168            fn main() {
 8169                sample(ˇ)
 8170            }
 8171        "
 8172        .unindent(),
 8173    );
 8174    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8175    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8176        .await;
 8177    cx.update_editor(|editor, _, _| {
 8178        let signature_help_state = editor.signature_help_state.popover().cloned();
 8179        assert!(signature_help_state.is_some());
 8180        let ParsedMarkdown {
 8181            text, highlights, ..
 8182        } = signature_help_state.unwrap().parsed_content;
 8183        assert_eq!(text, "param1: u8, param2: u8");
 8184        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8185        editor.signature_help_state = SignatureHelpState::default();
 8186    });
 8187
 8188    // Ensure that signature_help is called when auto signature help override is enabled
 8189    cx.update(|_, cx| {
 8190        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8191            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8192                settings.auto_signature_help = Some(true);
 8193                settings.show_signature_help_after_edits = Some(false);
 8194            });
 8195        });
 8196    });
 8197    cx.set_state(
 8198        &r#"
 8199            fn main() {
 8200                sampleˇ
 8201            }
 8202        "#
 8203        .unindent(),
 8204    );
 8205    cx.update_editor(|editor, window, cx| {
 8206        editor.handle_input("(", window, cx);
 8207    });
 8208    cx.assert_editor_state(
 8209        &"
 8210            fn main() {
 8211                sample(ˇ)
 8212            }
 8213        "
 8214        .unindent(),
 8215    );
 8216    handle_signature_help_request(&mut cx, mocked_response).await;
 8217    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8218        .await;
 8219    cx.editor(|editor, _, _| {
 8220        let signature_help_state = editor.signature_help_state.popover().cloned();
 8221        assert!(signature_help_state.is_some());
 8222        let ParsedMarkdown {
 8223            text, highlights, ..
 8224        } = signature_help_state.unwrap().parsed_content;
 8225        assert_eq!(text, "param1: u8, param2: u8");
 8226        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8227    });
 8228}
 8229
 8230#[gpui::test]
 8231async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8232    init_test(cx, |_| {});
 8233    cx.update(|cx| {
 8234        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8235            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8236                settings.auto_signature_help = Some(true);
 8237            });
 8238        });
 8239    });
 8240
 8241    let mut cx = EditorLspTestContext::new_rust(
 8242        lsp::ServerCapabilities {
 8243            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8244                ..Default::default()
 8245            }),
 8246            ..Default::default()
 8247        },
 8248        cx,
 8249    )
 8250    .await;
 8251
 8252    // A test that directly calls `show_signature_help`
 8253    cx.update_editor(|editor, window, cx| {
 8254        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8255    });
 8256
 8257    let mocked_response = lsp::SignatureHelp {
 8258        signatures: vec![lsp::SignatureInformation {
 8259            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8260            documentation: None,
 8261            parameters: Some(vec![
 8262                lsp::ParameterInformation {
 8263                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8264                    documentation: None,
 8265                },
 8266                lsp::ParameterInformation {
 8267                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8268                    documentation: None,
 8269                },
 8270            ]),
 8271            active_parameter: None,
 8272        }],
 8273        active_signature: Some(0),
 8274        active_parameter: Some(0),
 8275    };
 8276    handle_signature_help_request(&mut cx, mocked_response).await;
 8277
 8278    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8279        .await;
 8280
 8281    cx.editor(|editor, _, _| {
 8282        let signature_help_state = editor.signature_help_state.popover().cloned();
 8283        assert!(signature_help_state.is_some());
 8284        let ParsedMarkdown {
 8285            text, highlights, ..
 8286        } = signature_help_state.unwrap().parsed_content;
 8287        assert_eq!(text, "param1: u8, param2: u8");
 8288        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8289    });
 8290
 8291    // When exiting outside from inside the brackets, `signature_help` is closed.
 8292    cx.set_state(indoc! {"
 8293        fn main() {
 8294            sample(ˇ);
 8295        }
 8296
 8297        fn sample(param1: u8, param2: u8) {}
 8298    "});
 8299
 8300    cx.update_editor(|editor, window, cx| {
 8301        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8302    });
 8303
 8304    let mocked_response = lsp::SignatureHelp {
 8305        signatures: Vec::new(),
 8306        active_signature: None,
 8307        active_parameter: None,
 8308    };
 8309    handle_signature_help_request(&mut cx, mocked_response).await;
 8310
 8311    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8312        .await;
 8313
 8314    cx.editor(|editor, _, _| {
 8315        assert!(!editor.signature_help_state.is_shown());
 8316    });
 8317
 8318    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8319    cx.set_state(indoc! {"
 8320        fn main() {
 8321            sample(ˇ);
 8322        }
 8323
 8324        fn sample(param1: u8, param2: u8) {}
 8325    "});
 8326
 8327    let mocked_response = lsp::SignatureHelp {
 8328        signatures: vec![lsp::SignatureInformation {
 8329            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8330            documentation: None,
 8331            parameters: Some(vec![
 8332                lsp::ParameterInformation {
 8333                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8334                    documentation: None,
 8335                },
 8336                lsp::ParameterInformation {
 8337                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8338                    documentation: None,
 8339                },
 8340            ]),
 8341            active_parameter: None,
 8342        }],
 8343        active_signature: Some(0),
 8344        active_parameter: Some(0),
 8345    };
 8346    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8347    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8348        .await;
 8349    cx.editor(|editor, _, _| {
 8350        assert!(editor.signature_help_state.is_shown());
 8351    });
 8352
 8353    // Restore the popover with more parameter input
 8354    cx.set_state(indoc! {"
 8355        fn main() {
 8356            sample(param1, param2ˇ);
 8357        }
 8358
 8359        fn sample(param1: u8, param2: u8) {}
 8360    "});
 8361
 8362    let mocked_response = lsp::SignatureHelp {
 8363        signatures: vec![lsp::SignatureInformation {
 8364            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8365            documentation: None,
 8366            parameters: Some(vec![
 8367                lsp::ParameterInformation {
 8368                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8369                    documentation: None,
 8370                },
 8371                lsp::ParameterInformation {
 8372                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8373                    documentation: None,
 8374                },
 8375            ]),
 8376            active_parameter: None,
 8377        }],
 8378        active_signature: Some(0),
 8379        active_parameter: Some(1),
 8380    };
 8381    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8382    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8383        .await;
 8384
 8385    // When selecting a range, the popover is gone.
 8386    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8387    cx.update_editor(|editor, window, cx| {
 8388        editor.change_selections(None, window, cx, |s| {
 8389            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8390        })
 8391    });
 8392    cx.assert_editor_state(indoc! {"
 8393        fn main() {
 8394            sample(param1, «ˇparam2»);
 8395        }
 8396
 8397        fn sample(param1: u8, param2: u8) {}
 8398    "});
 8399    cx.editor(|editor, _, _| {
 8400        assert!(!editor.signature_help_state.is_shown());
 8401    });
 8402
 8403    // When unselecting again, the popover is back if within the brackets.
 8404    cx.update_editor(|editor, window, cx| {
 8405        editor.change_selections(None, window, cx, |s| {
 8406            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8407        })
 8408    });
 8409    cx.assert_editor_state(indoc! {"
 8410        fn main() {
 8411            sample(param1, ˇparam2);
 8412        }
 8413
 8414        fn sample(param1: u8, param2: u8) {}
 8415    "});
 8416    handle_signature_help_request(&mut cx, mocked_response).await;
 8417    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8418        .await;
 8419    cx.editor(|editor, _, _| {
 8420        assert!(editor.signature_help_state.is_shown());
 8421    });
 8422
 8423    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8424    cx.update_editor(|editor, window, cx| {
 8425        editor.change_selections(None, window, cx, |s| {
 8426            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8427            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8428        })
 8429    });
 8430    cx.assert_editor_state(indoc! {"
 8431        fn main() {
 8432            sample(param1, ˇparam2);
 8433        }
 8434
 8435        fn sample(param1: u8, param2: u8) {}
 8436    "});
 8437
 8438    let mocked_response = lsp::SignatureHelp {
 8439        signatures: vec![lsp::SignatureInformation {
 8440            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8441            documentation: None,
 8442            parameters: Some(vec![
 8443                lsp::ParameterInformation {
 8444                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8445                    documentation: None,
 8446                },
 8447                lsp::ParameterInformation {
 8448                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8449                    documentation: None,
 8450                },
 8451            ]),
 8452            active_parameter: None,
 8453        }],
 8454        active_signature: Some(0),
 8455        active_parameter: Some(1),
 8456    };
 8457    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8458    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8459        .await;
 8460    cx.update_editor(|editor, _, cx| {
 8461        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8462    });
 8463    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8464        .await;
 8465    cx.update_editor(|editor, window, cx| {
 8466        editor.change_selections(None, window, cx, |s| {
 8467            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8468        })
 8469    });
 8470    cx.assert_editor_state(indoc! {"
 8471        fn main() {
 8472            sample(param1, «ˇparam2»);
 8473        }
 8474
 8475        fn sample(param1: u8, param2: u8) {}
 8476    "});
 8477    cx.update_editor(|editor, window, cx| {
 8478        editor.change_selections(None, window, cx, |s| {
 8479            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8480        })
 8481    });
 8482    cx.assert_editor_state(indoc! {"
 8483        fn main() {
 8484            sample(param1, ˇparam2);
 8485        }
 8486
 8487        fn sample(param1: u8, param2: u8) {}
 8488    "});
 8489    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8490        .await;
 8491}
 8492
 8493#[gpui::test]
 8494async fn test_completion(cx: &mut gpui::TestAppContext) {
 8495    init_test(cx, |_| {});
 8496
 8497    let mut cx = EditorLspTestContext::new_rust(
 8498        lsp::ServerCapabilities {
 8499            completion_provider: Some(lsp::CompletionOptions {
 8500                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8501                resolve_provider: Some(true),
 8502                ..Default::default()
 8503            }),
 8504            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8505            ..Default::default()
 8506        },
 8507        cx,
 8508    )
 8509    .await;
 8510    let counter = Arc::new(AtomicUsize::new(0));
 8511
 8512    cx.set_state(indoc! {"
 8513        oneˇ
 8514        two
 8515        three
 8516    "});
 8517    cx.simulate_keystroke(".");
 8518    handle_completion_request(
 8519        &mut cx,
 8520        indoc! {"
 8521            one.|<>
 8522            two
 8523            three
 8524        "},
 8525        vec!["first_completion", "second_completion"],
 8526        counter.clone(),
 8527    )
 8528    .await;
 8529    cx.condition(|editor, _| editor.context_menu_visible())
 8530        .await;
 8531    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8532
 8533    let _handler = handle_signature_help_request(
 8534        &mut cx,
 8535        lsp::SignatureHelp {
 8536            signatures: vec![lsp::SignatureInformation {
 8537                label: "test signature".to_string(),
 8538                documentation: None,
 8539                parameters: Some(vec![lsp::ParameterInformation {
 8540                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8541                    documentation: None,
 8542                }]),
 8543                active_parameter: None,
 8544            }],
 8545            active_signature: None,
 8546            active_parameter: None,
 8547        },
 8548    );
 8549    cx.update_editor(|editor, window, cx| {
 8550        assert!(
 8551            !editor.signature_help_state.is_shown(),
 8552            "No signature help was called for"
 8553        );
 8554        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8555    });
 8556    cx.run_until_parked();
 8557    cx.update_editor(|editor, _, _| {
 8558        assert!(
 8559            !editor.signature_help_state.is_shown(),
 8560            "No signature help should be shown when completions menu is open"
 8561        );
 8562    });
 8563
 8564    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8565        editor.context_menu_next(&Default::default(), window, cx);
 8566        editor
 8567            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8568            .unwrap()
 8569    });
 8570    cx.assert_editor_state(indoc! {"
 8571        one.second_completionˇ
 8572        two
 8573        three
 8574    "});
 8575
 8576    handle_resolve_completion_request(
 8577        &mut cx,
 8578        Some(vec![
 8579            (
 8580                //This overlaps with the primary completion edit which is
 8581                //misbehavior from the LSP spec, test that we filter it out
 8582                indoc! {"
 8583                    one.second_ˇcompletion
 8584                    two
 8585                    threeˇ
 8586                "},
 8587                "overlapping additional edit",
 8588            ),
 8589            (
 8590                indoc! {"
 8591                    one.second_completion
 8592                    two
 8593                    threeˇ
 8594                "},
 8595                "\nadditional edit",
 8596            ),
 8597        ]),
 8598    )
 8599    .await;
 8600    apply_additional_edits.await.unwrap();
 8601    cx.assert_editor_state(indoc! {"
 8602        one.second_completionˇ
 8603        two
 8604        three
 8605        additional edit
 8606    "});
 8607
 8608    cx.set_state(indoc! {"
 8609        one.second_completion
 8610        twoˇ
 8611        threeˇ
 8612        additional edit
 8613    "});
 8614    cx.simulate_keystroke(" ");
 8615    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8616    cx.simulate_keystroke("s");
 8617    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8618
 8619    cx.assert_editor_state(indoc! {"
 8620        one.second_completion
 8621        two sˇ
 8622        three sˇ
 8623        additional edit
 8624    "});
 8625    handle_completion_request(
 8626        &mut cx,
 8627        indoc! {"
 8628            one.second_completion
 8629            two s
 8630            three <s|>
 8631            additional edit
 8632        "},
 8633        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8634        counter.clone(),
 8635    )
 8636    .await;
 8637    cx.condition(|editor, _| editor.context_menu_visible())
 8638        .await;
 8639    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8640
 8641    cx.simulate_keystroke("i");
 8642
 8643    handle_completion_request(
 8644        &mut cx,
 8645        indoc! {"
 8646            one.second_completion
 8647            two si
 8648            three <si|>
 8649            additional edit
 8650        "},
 8651        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8652        counter.clone(),
 8653    )
 8654    .await;
 8655    cx.condition(|editor, _| editor.context_menu_visible())
 8656        .await;
 8657    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8658
 8659    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8660        editor
 8661            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8662            .unwrap()
 8663    });
 8664    cx.assert_editor_state(indoc! {"
 8665        one.second_completion
 8666        two sixth_completionˇ
 8667        three sixth_completionˇ
 8668        additional edit
 8669    "});
 8670
 8671    apply_additional_edits.await.unwrap();
 8672
 8673    update_test_language_settings(&mut cx, |settings| {
 8674        settings.defaults.show_completions_on_input = Some(false);
 8675    });
 8676    cx.set_state("editorˇ");
 8677    cx.simulate_keystroke(".");
 8678    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8679    cx.simulate_keystroke("c");
 8680    cx.simulate_keystroke("l");
 8681    cx.simulate_keystroke("o");
 8682    cx.assert_editor_state("editor.cloˇ");
 8683    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8684    cx.update_editor(|editor, window, cx| {
 8685        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8686    });
 8687    handle_completion_request(
 8688        &mut cx,
 8689        "editor.<clo|>",
 8690        vec!["close", "clobber"],
 8691        counter.clone(),
 8692    )
 8693    .await;
 8694    cx.condition(|editor, _| editor.context_menu_visible())
 8695        .await;
 8696    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8697
 8698    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8699        editor
 8700            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8701            .unwrap()
 8702    });
 8703    cx.assert_editor_state("editor.closeˇ");
 8704    handle_resolve_completion_request(&mut cx, None).await;
 8705    apply_additional_edits.await.unwrap();
 8706}
 8707
 8708#[gpui::test]
 8709async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8710    init_test(cx, |_| {});
 8711
 8712    let fs = FakeFs::new(cx.executor());
 8713    fs.insert_tree(
 8714        "/a",
 8715        json!({
 8716            "main.ts": "a",
 8717        }),
 8718    )
 8719    .await;
 8720
 8721    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8722    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8723    let typescript_language = Arc::new(Language::new(
 8724        LanguageConfig {
 8725            name: "TypeScript".into(),
 8726            matcher: LanguageMatcher {
 8727                path_suffixes: vec!["ts".to_string()],
 8728                ..LanguageMatcher::default()
 8729            },
 8730            line_comments: vec!["// ".into()],
 8731            ..LanguageConfig::default()
 8732        },
 8733        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8734    ));
 8735    language_registry.add(typescript_language.clone());
 8736    let mut fake_servers = language_registry.register_fake_lsp(
 8737        "TypeScript",
 8738        FakeLspAdapter {
 8739            capabilities: lsp::ServerCapabilities {
 8740                completion_provider: Some(lsp::CompletionOptions {
 8741                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8742                    ..lsp::CompletionOptions::default()
 8743                }),
 8744                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8745                ..lsp::ServerCapabilities::default()
 8746            },
 8747            // Emulate vtsls label generation
 8748            label_for_completion: Some(Box::new(|item, _| {
 8749                let text = if let Some(description) = item
 8750                    .label_details
 8751                    .as_ref()
 8752                    .and_then(|label_details| label_details.description.as_ref())
 8753                {
 8754                    format!("{} {}", item.label, description)
 8755                } else if let Some(detail) = &item.detail {
 8756                    format!("{} {}", item.label, detail)
 8757                } else {
 8758                    item.label.clone()
 8759                };
 8760                let len = text.len();
 8761                Some(language::CodeLabel {
 8762                    text,
 8763                    runs: Vec::new(),
 8764                    filter_range: 0..len,
 8765                })
 8766            })),
 8767            ..FakeLspAdapter::default()
 8768        },
 8769    );
 8770    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8771    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8772    let worktree_id = workspace
 8773        .update(cx, |workspace, _window, cx| {
 8774            workspace.project().update(cx, |project, cx| {
 8775                project.worktrees(cx).next().unwrap().read(cx).id()
 8776            })
 8777        })
 8778        .unwrap();
 8779    let _buffer = project
 8780        .update(cx, |project, cx| {
 8781            project.open_local_buffer_with_lsp("/a/main.ts", cx)
 8782        })
 8783        .await
 8784        .unwrap();
 8785    let editor = workspace
 8786        .update(cx, |workspace, window, cx| {
 8787            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8788        })
 8789        .unwrap()
 8790        .await
 8791        .unwrap()
 8792        .downcast::<Editor>()
 8793        .unwrap();
 8794    let fake_server = fake_servers.next().await.unwrap();
 8795
 8796    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8797    let multiline_label_2 = "a\nb\nc\n";
 8798    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8799    let multiline_description = "d\ne\nf\n";
 8800    let multiline_detail_2 = "g\nh\ni\n";
 8801
 8802    let mut completion_handle =
 8803        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8804            Ok(Some(lsp::CompletionResponse::Array(vec![
 8805                lsp::CompletionItem {
 8806                    label: multiline_label.to_string(),
 8807                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8808                        range: lsp::Range {
 8809                            start: lsp::Position {
 8810                                line: params.text_document_position.position.line,
 8811                                character: params.text_document_position.position.character,
 8812                            },
 8813                            end: lsp::Position {
 8814                                line: params.text_document_position.position.line,
 8815                                character: params.text_document_position.position.character,
 8816                            },
 8817                        },
 8818                        new_text: "new_text_1".to_string(),
 8819                    })),
 8820                    ..lsp::CompletionItem::default()
 8821                },
 8822                lsp::CompletionItem {
 8823                    label: "single line label 1".to_string(),
 8824                    detail: Some(multiline_detail.to_string()),
 8825                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8826                        range: lsp::Range {
 8827                            start: lsp::Position {
 8828                                line: params.text_document_position.position.line,
 8829                                character: params.text_document_position.position.character,
 8830                            },
 8831                            end: lsp::Position {
 8832                                line: params.text_document_position.position.line,
 8833                                character: params.text_document_position.position.character,
 8834                            },
 8835                        },
 8836                        new_text: "new_text_2".to_string(),
 8837                    })),
 8838                    ..lsp::CompletionItem::default()
 8839                },
 8840                lsp::CompletionItem {
 8841                    label: "single line label 2".to_string(),
 8842                    label_details: Some(lsp::CompletionItemLabelDetails {
 8843                        description: Some(multiline_description.to_string()),
 8844                        detail: None,
 8845                    }),
 8846                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8847                        range: lsp::Range {
 8848                            start: lsp::Position {
 8849                                line: params.text_document_position.position.line,
 8850                                character: params.text_document_position.position.character,
 8851                            },
 8852                            end: lsp::Position {
 8853                                line: params.text_document_position.position.line,
 8854                                character: params.text_document_position.position.character,
 8855                            },
 8856                        },
 8857                        new_text: "new_text_2".to_string(),
 8858                    })),
 8859                    ..lsp::CompletionItem::default()
 8860                },
 8861                lsp::CompletionItem {
 8862                    label: multiline_label_2.to_string(),
 8863                    detail: Some(multiline_detail_2.to_string()),
 8864                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8865                        range: lsp::Range {
 8866                            start: lsp::Position {
 8867                                line: params.text_document_position.position.line,
 8868                                character: params.text_document_position.position.character,
 8869                            },
 8870                            end: lsp::Position {
 8871                                line: params.text_document_position.position.line,
 8872                                character: params.text_document_position.position.character,
 8873                            },
 8874                        },
 8875                        new_text: "new_text_3".to_string(),
 8876                    })),
 8877                    ..lsp::CompletionItem::default()
 8878                },
 8879                lsp::CompletionItem {
 8880                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8881                    detail: Some(
 8882                        "Details with many     spaces and \t but without newlines".to_string(),
 8883                    ),
 8884                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8885                        range: lsp::Range {
 8886                            start: lsp::Position {
 8887                                line: params.text_document_position.position.line,
 8888                                character: params.text_document_position.position.character,
 8889                            },
 8890                            end: lsp::Position {
 8891                                line: params.text_document_position.position.line,
 8892                                character: params.text_document_position.position.character,
 8893                            },
 8894                        },
 8895                        new_text: "new_text_4".to_string(),
 8896                    })),
 8897                    ..lsp::CompletionItem::default()
 8898                },
 8899            ])))
 8900        });
 8901
 8902    editor.update_in(cx, |editor, window, cx| {
 8903        cx.focus_self(window);
 8904        editor.move_to_end(&MoveToEnd, window, cx);
 8905        editor.handle_input(".", window, cx);
 8906    });
 8907    cx.run_until_parked();
 8908    completion_handle.next().await.unwrap();
 8909
 8910    editor.update(cx, |editor, _| {
 8911        assert!(editor.context_menu_visible());
 8912        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8913        {
 8914            let completion_labels = menu
 8915                .completions
 8916                .borrow()
 8917                .iter()
 8918                .map(|c| c.label.text.clone())
 8919                .collect::<Vec<_>>();
 8920            assert_eq!(
 8921                completion_labels,
 8922                &[
 8923                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8924                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8925                    "single line label 2 d e f ",
 8926                    "a b c g h i ",
 8927                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8928                ],
 8929                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8930            );
 8931
 8932            for completion in menu
 8933                .completions
 8934                .borrow()
 8935                .iter() {
 8936                    assert_eq!(
 8937                        completion.label.filter_range,
 8938                        0..completion.label.text.len(),
 8939                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8940                    );
 8941                }
 8942
 8943        } else {
 8944            panic!("expected completion menu to be open");
 8945        }
 8946    });
 8947}
 8948
 8949#[gpui::test]
 8950async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8951    init_test(cx, |_| {});
 8952    let mut cx = EditorLspTestContext::new_rust(
 8953        lsp::ServerCapabilities {
 8954            completion_provider: Some(lsp::CompletionOptions {
 8955                trigger_characters: Some(vec![".".to_string()]),
 8956                ..Default::default()
 8957            }),
 8958            ..Default::default()
 8959        },
 8960        cx,
 8961    )
 8962    .await;
 8963    cx.lsp
 8964        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8965            Ok(Some(lsp::CompletionResponse::Array(vec![
 8966                lsp::CompletionItem {
 8967                    label: "first".into(),
 8968                    ..Default::default()
 8969                },
 8970                lsp::CompletionItem {
 8971                    label: "last".into(),
 8972                    ..Default::default()
 8973                },
 8974            ])))
 8975        });
 8976    cx.set_state("variableˇ");
 8977    cx.simulate_keystroke(".");
 8978    cx.executor().run_until_parked();
 8979
 8980    cx.update_editor(|editor, _, _| {
 8981        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8982        {
 8983            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 8984        } else {
 8985            panic!("expected completion menu to be open");
 8986        }
 8987    });
 8988
 8989    cx.update_editor(|editor, window, cx| {
 8990        editor.move_page_down(&MovePageDown::default(), window, cx);
 8991        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8992        {
 8993            assert!(
 8994                menu.selected_item == 1,
 8995                "expected PageDown to select the last item from the context menu"
 8996            );
 8997        } else {
 8998            panic!("expected completion menu to stay open after PageDown");
 8999        }
 9000    });
 9001
 9002    cx.update_editor(|editor, window, cx| {
 9003        editor.move_page_up(&MovePageUp::default(), window, cx);
 9004        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9005        {
 9006            assert!(
 9007                menu.selected_item == 0,
 9008                "expected PageUp to select the first item from the context menu"
 9009            );
 9010        } else {
 9011            panic!("expected completion menu to stay open after PageUp");
 9012        }
 9013    });
 9014}
 9015
 9016#[gpui::test]
 9017async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9018    init_test(cx, |_| {});
 9019    let mut cx = EditorLspTestContext::new_rust(
 9020        lsp::ServerCapabilities {
 9021            completion_provider: Some(lsp::CompletionOptions {
 9022                trigger_characters: Some(vec![".".to_string()]),
 9023                ..Default::default()
 9024            }),
 9025            ..Default::default()
 9026        },
 9027        cx,
 9028    )
 9029    .await;
 9030    cx.lsp
 9031        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9032            Ok(Some(lsp::CompletionResponse::Array(vec![
 9033                lsp::CompletionItem {
 9034                    label: "Range".into(),
 9035                    sort_text: Some("a".into()),
 9036                    ..Default::default()
 9037                },
 9038                lsp::CompletionItem {
 9039                    label: "r".into(),
 9040                    sort_text: Some("b".into()),
 9041                    ..Default::default()
 9042                },
 9043                lsp::CompletionItem {
 9044                    label: "ret".into(),
 9045                    sort_text: Some("c".into()),
 9046                    ..Default::default()
 9047                },
 9048                lsp::CompletionItem {
 9049                    label: "return".into(),
 9050                    sort_text: Some("d".into()),
 9051                    ..Default::default()
 9052                },
 9053                lsp::CompletionItem {
 9054                    label: "slice".into(),
 9055                    sort_text: Some("d".into()),
 9056                    ..Default::default()
 9057                },
 9058            ])))
 9059        });
 9060    cx.set_state("");
 9061    cx.executor().run_until_parked();
 9062    cx.update_editor(|editor, window, cx| {
 9063        editor.show_completions(
 9064            &ShowCompletions {
 9065                trigger: Some("r".into()),
 9066            },
 9067            window,
 9068            cx,
 9069        );
 9070    });
 9071    cx.executor().run_until_parked();
 9072
 9073    cx.update_editor(|editor, _, _| {
 9074        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9075        {
 9076            assert_eq!(
 9077                completion_menu_entries(&menu),
 9078                &["r", "ret", "Range", "return"]
 9079            );
 9080        } else {
 9081            panic!("expected completion menu to be open");
 9082        }
 9083    });
 9084}
 9085
 9086#[gpui::test]
 9087async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9088    init_test(cx, |_| {});
 9089
 9090    let mut cx = EditorLspTestContext::new_rust(
 9091        lsp::ServerCapabilities {
 9092            completion_provider: Some(lsp::CompletionOptions {
 9093                trigger_characters: Some(vec![".".to_string()]),
 9094                resolve_provider: Some(true),
 9095                ..Default::default()
 9096            }),
 9097            ..Default::default()
 9098        },
 9099        cx,
 9100    )
 9101    .await;
 9102
 9103    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9104    cx.simulate_keystroke(".");
 9105    let completion_item = lsp::CompletionItem {
 9106        label: "Some".into(),
 9107        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9108        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9109        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9110            kind: lsp::MarkupKind::Markdown,
 9111            value: "```rust\nSome(2)\n```".to_string(),
 9112        })),
 9113        deprecated: Some(false),
 9114        sort_text: Some("Some".to_string()),
 9115        filter_text: Some("Some".to_string()),
 9116        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9117        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9118            range: lsp::Range {
 9119                start: lsp::Position {
 9120                    line: 0,
 9121                    character: 22,
 9122                },
 9123                end: lsp::Position {
 9124                    line: 0,
 9125                    character: 22,
 9126                },
 9127            },
 9128            new_text: "Some(2)".to_string(),
 9129        })),
 9130        additional_text_edits: Some(vec![lsp::TextEdit {
 9131            range: lsp::Range {
 9132                start: lsp::Position {
 9133                    line: 0,
 9134                    character: 20,
 9135                },
 9136                end: lsp::Position {
 9137                    line: 0,
 9138                    character: 22,
 9139                },
 9140            },
 9141            new_text: "".to_string(),
 9142        }]),
 9143        ..Default::default()
 9144    };
 9145
 9146    let closure_completion_item = completion_item.clone();
 9147    let counter = Arc::new(AtomicUsize::new(0));
 9148    let counter_clone = counter.clone();
 9149    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9150        let task_completion_item = closure_completion_item.clone();
 9151        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9152        async move {
 9153            Ok(Some(lsp::CompletionResponse::Array(vec![
 9154                task_completion_item,
 9155            ])))
 9156        }
 9157    });
 9158
 9159    cx.condition(|editor, _| editor.context_menu_visible())
 9160        .await;
 9161    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9162    assert!(request.next().await.is_some());
 9163    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9164
 9165    cx.simulate_keystroke("S");
 9166    cx.simulate_keystroke("o");
 9167    cx.simulate_keystroke("m");
 9168    cx.condition(|editor, _| editor.context_menu_visible())
 9169        .await;
 9170    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9171    assert!(request.next().await.is_some());
 9172    assert!(request.next().await.is_some());
 9173    assert!(request.next().await.is_some());
 9174    request.close();
 9175    assert!(request.next().await.is_none());
 9176    assert_eq!(
 9177        counter.load(atomic::Ordering::Acquire),
 9178        4,
 9179        "With the completions menu open, only one LSP request should happen per input"
 9180    );
 9181}
 9182
 9183#[gpui::test]
 9184async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9185    init_test(cx, |_| {});
 9186    let mut cx = EditorTestContext::new(cx).await;
 9187    let language = Arc::new(Language::new(
 9188        LanguageConfig {
 9189            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9190            ..Default::default()
 9191        },
 9192        Some(tree_sitter_rust::LANGUAGE.into()),
 9193    ));
 9194    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9195
 9196    // If multiple selections intersect a line, the line is only toggled once.
 9197    cx.set_state(indoc! {"
 9198        fn a() {
 9199            «//b();
 9200            ˇ»// «c();
 9201            //ˇ»  d();
 9202        }
 9203    "});
 9204
 9205    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9206
 9207    cx.assert_editor_state(indoc! {"
 9208        fn a() {
 9209            «b();
 9210            c();
 9211            ˇ» d();
 9212        }
 9213    "});
 9214
 9215    // The comment prefix is inserted at the same column for every line in a
 9216    // selection.
 9217    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9218
 9219    cx.assert_editor_state(indoc! {"
 9220        fn a() {
 9221            // «b();
 9222            // c();
 9223            ˇ»//  d();
 9224        }
 9225    "});
 9226
 9227    // If a selection ends at the beginning of a line, that line is not toggled.
 9228    cx.set_selections_state(indoc! {"
 9229        fn a() {
 9230            // b();
 9231            «// c();
 9232        ˇ»    //  d();
 9233        }
 9234    "});
 9235
 9236    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9237
 9238    cx.assert_editor_state(indoc! {"
 9239        fn a() {
 9240            // b();
 9241            «c();
 9242        ˇ»    //  d();
 9243        }
 9244    "});
 9245
 9246    // If a selection span a single line and is empty, the line is toggled.
 9247    cx.set_state(indoc! {"
 9248        fn a() {
 9249            a();
 9250            b();
 9251        ˇ
 9252        }
 9253    "});
 9254
 9255    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9256
 9257    cx.assert_editor_state(indoc! {"
 9258        fn a() {
 9259            a();
 9260            b();
 9261        //•ˇ
 9262        }
 9263    "});
 9264
 9265    // If a selection span multiple lines, empty lines are not toggled.
 9266    cx.set_state(indoc! {"
 9267        fn a() {
 9268            «a();
 9269
 9270            c();ˇ»
 9271        }
 9272    "});
 9273
 9274    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9275
 9276    cx.assert_editor_state(indoc! {"
 9277        fn a() {
 9278            // «a();
 9279
 9280            // c();ˇ»
 9281        }
 9282    "});
 9283
 9284    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9285    cx.set_state(indoc! {"
 9286        fn a() {
 9287            «// a();
 9288            /// b();
 9289            //! c();ˇ»
 9290        }
 9291    "});
 9292
 9293    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9294
 9295    cx.assert_editor_state(indoc! {"
 9296        fn a() {
 9297            «a();
 9298            b();
 9299            c();ˇ»
 9300        }
 9301    "});
 9302}
 9303
 9304#[gpui::test]
 9305async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9306    init_test(cx, |_| {});
 9307    let mut cx = EditorTestContext::new(cx).await;
 9308    let language = Arc::new(Language::new(
 9309        LanguageConfig {
 9310            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9311            ..Default::default()
 9312        },
 9313        Some(tree_sitter_rust::LANGUAGE.into()),
 9314    ));
 9315    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9316
 9317    let toggle_comments = &ToggleComments {
 9318        advance_downwards: false,
 9319        ignore_indent: true,
 9320    };
 9321
 9322    // If multiple selections intersect a line, the line is only toggled once.
 9323    cx.set_state(indoc! {"
 9324        fn a() {
 9325        //    «b();
 9326        //    c();
 9327        //    ˇ» d();
 9328        }
 9329    "});
 9330
 9331    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9332
 9333    cx.assert_editor_state(indoc! {"
 9334        fn a() {
 9335            «b();
 9336            c();
 9337            ˇ» d();
 9338        }
 9339    "});
 9340
 9341    // The comment prefix is inserted at the beginning of each line
 9342    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9343
 9344    cx.assert_editor_state(indoc! {"
 9345        fn a() {
 9346        //    «b();
 9347        //    c();
 9348        //    ˇ» d();
 9349        }
 9350    "});
 9351
 9352    // If a selection ends at the beginning of a line, that line is not toggled.
 9353    cx.set_selections_state(indoc! {"
 9354        fn a() {
 9355        //    b();
 9356        //    «c();
 9357        ˇ»//     d();
 9358        }
 9359    "});
 9360
 9361    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9362
 9363    cx.assert_editor_state(indoc! {"
 9364        fn a() {
 9365        //    b();
 9366            «c();
 9367        ˇ»//     d();
 9368        }
 9369    "});
 9370
 9371    // If a selection span a single line and is empty, the line is toggled.
 9372    cx.set_state(indoc! {"
 9373        fn a() {
 9374            a();
 9375            b();
 9376        ˇ
 9377        }
 9378    "});
 9379
 9380    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9381
 9382    cx.assert_editor_state(indoc! {"
 9383        fn a() {
 9384            a();
 9385            b();
 9386        //ˇ
 9387        }
 9388    "});
 9389
 9390    // If a selection span multiple lines, empty lines are not toggled.
 9391    cx.set_state(indoc! {"
 9392        fn a() {
 9393            «a();
 9394
 9395            c();ˇ»
 9396        }
 9397    "});
 9398
 9399    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9400
 9401    cx.assert_editor_state(indoc! {"
 9402        fn a() {
 9403        //    «a();
 9404
 9405        //    c();ˇ»
 9406        }
 9407    "});
 9408
 9409    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9410    cx.set_state(indoc! {"
 9411        fn a() {
 9412        //    «a();
 9413        ///    b();
 9414        //!    c();ˇ»
 9415        }
 9416    "});
 9417
 9418    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9419
 9420    cx.assert_editor_state(indoc! {"
 9421        fn a() {
 9422            «a();
 9423            b();
 9424            c();ˇ»
 9425        }
 9426    "});
 9427}
 9428
 9429#[gpui::test]
 9430async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9431    init_test(cx, |_| {});
 9432
 9433    let language = Arc::new(Language::new(
 9434        LanguageConfig {
 9435            line_comments: vec!["// ".into()],
 9436            ..Default::default()
 9437        },
 9438        Some(tree_sitter_rust::LANGUAGE.into()),
 9439    ));
 9440
 9441    let mut cx = EditorTestContext::new(cx).await;
 9442
 9443    cx.language_registry().add(language.clone());
 9444    cx.update_buffer(|buffer, cx| {
 9445        buffer.set_language(Some(language), cx);
 9446    });
 9447
 9448    let toggle_comments = &ToggleComments {
 9449        advance_downwards: true,
 9450        ignore_indent: false,
 9451    };
 9452
 9453    // Single cursor on one line -> advance
 9454    // Cursor moves horizontally 3 characters as well on non-blank line
 9455    cx.set_state(indoc!(
 9456        "fn a() {
 9457             ˇdog();
 9458             cat();
 9459        }"
 9460    ));
 9461    cx.update_editor(|editor, window, cx| {
 9462        editor.toggle_comments(toggle_comments, window, cx);
 9463    });
 9464    cx.assert_editor_state(indoc!(
 9465        "fn a() {
 9466             // dog();
 9467             catˇ();
 9468        }"
 9469    ));
 9470
 9471    // Single selection on one line -> don't advance
 9472    cx.set_state(indoc!(
 9473        "fn a() {
 9474             «dog()ˇ»;
 9475             cat();
 9476        }"
 9477    ));
 9478    cx.update_editor(|editor, window, cx| {
 9479        editor.toggle_comments(toggle_comments, window, cx);
 9480    });
 9481    cx.assert_editor_state(indoc!(
 9482        "fn a() {
 9483             // «dog()ˇ»;
 9484             cat();
 9485        }"
 9486    ));
 9487
 9488    // Multiple cursors on one line -> advance
 9489    cx.set_state(indoc!(
 9490        "fn a() {
 9491             ˇdˇog();
 9492             cat();
 9493        }"
 9494    ));
 9495    cx.update_editor(|editor, window, cx| {
 9496        editor.toggle_comments(toggle_comments, window, cx);
 9497    });
 9498    cx.assert_editor_state(indoc!(
 9499        "fn a() {
 9500             // dog();
 9501             catˇ(ˇ);
 9502        }"
 9503    ));
 9504
 9505    // Multiple cursors on one line, with selection -> don't advance
 9506    cx.set_state(indoc!(
 9507        "fn a() {
 9508             ˇdˇog«()ˇ»;
 9509             cat();
 9510        }"
 9511    ));
 9512    cx.update_editor(|editor, window, cx| {
 9513        editor.toggle_comments(toggle_comments, window, cx);
 9514    });
 9515    cx.assert_editor_state(indoc!(
 9516        "fn a() {
 9517             // ˇdˇog«()ˇ»;
 9518             cat();
 9519        }"
 9520    ));
 9521
 9522    // Single cursor on one line -> advance
 9523    // Cursor moves to column 0 on blank line
 9524    cx.set_state(indoc!(
 9525        "fn a() {
 9526             ˇdog();
 9527
 9528             cat();
 9529        }"
 9530    ));
 9531    cx.update_editor(|editor, window, cx| {
 9532        editor.toggle_comments(toggle_comments, window, cx);
 9533    });
 9534    cx.assert_editor_state(indoc!(
 9535        "fn a() {
 9536             // dog();
 9537        ˇ
 9538             cat();
 9539        }"
 9540    ));
 9541
 9542    // Single cursor on one line -> advance
 9543    // Cursor starts and ends at column 0
 9544    cx.set_state(indoc!(
 9545        "fn a() {
 9546         ˇ    dog();
 9547             cat();
 9548        }"
 9549    ));
 9550    cx.update_editor(|editor, window, cx| {
 9551        editor.toggle_comments(toggle_comments, window, cx);
 9552    });
 9553    cx.assert_editor_state(indoc!(
 9554        "fn a() {
 9555             // dog();
 9556         ˇ    cat();
 9557        }"
 9558    ));
 9559}
 9560
 9561#[gpui::test]
 9562async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9563    init_test(cx, |_| {});
 9564
 9565    let mut cx = EditorTestContext::new(cx).await;
 9566
 9567    let html_language = Arc::new(
 9568        Language::new(
 9569            LanguageConfig {
 9570                name: "HTML".into(),
 9571                block_comment: Some(("<!-- ".into(), " -->".into())),
 9572                ..Default::default()
 9573            },
 9574            Some(tree_sitter_html::language()),
 9575        )
 9576        .with_injection_query(
 9577            r#"
 9578            (script_element
 9579                (raw_text) @injection.content
 9580                (#set! injection.language "javascript"))
 9581            "#,
 9582        )
 9583        .unwrap(),
 9584    );
 9585
 9586    let javascript_language = Arc::new(Language::new(
 9587        LanguageConfig {
 9588            name: "JavaScript".into(),
 9589            line_comments: vec!["// ".into()],
 9590            ..Default::default()
 9591        },
 9592        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9593    ));
 9594
 9595    cx.language_registry().add(html_language.clone());
 9596    cx.language_registry().add(javascript_language.clone());
 9597    cx.update_buffer(|buffer, cx| {
 9598        buffer.set_language(Some(html_language), cx);
 9599    });
 9600
 9601    // Toggle comments for empty selections
 9602    cx.set_state(
 9603        &r#"
 9604            <p>A</p>ˇ
 9605            <p>B</p>ˇ
 9606            <p>C</p>ˇ
 9607        "#
 9608        .unindent(),
 9609    );
 9610    cx.update_editor(|editor, window, cx| {
 9611        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9612    });
 9613    cx.assert_editor_state(
 9614        &r#"
 9615            <!-- <p>A</p>ˇ -->
 9616            <!-- <p>B</p>ˇ -->
 9617            <!-- <p>C</p>ˇ -->
 9618        "#
 9619        .unindent(),
 9620    );
 9621    cx.update_editor(|editor, window, cx| {
 9622        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9623    });
 9624    cx.assert_editor_state(
 9625        &r#"
 9626            <p>A</p>ˇ
 9627            <p>B</p>ˇ
 9628            <p>C</p>ˇ
 9629        "#
 9630        .unindent(),
 9631    );
 9632
 9633    // Toggle comments for mixture of empty and non-empty selections, where
 9634    // multiple selections occupy a given line.
 9635    cx.set_state(
 9636        &r#"
 9637            <p>A«</p>
 9638            <p>ˇ»B</p>ˇ
 9639            <p>C«</p>
 9640            <p>ˇ»D</p>ˇ
 9641        "#
 9642        .unindent(),
 9643    );
 9644
 9645    cx.update_editor(|editor, window, cx| {
 9646        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9647    });
 9648    cx.assert_editor_state(
 9649        &r#"
 9650            <!-- <p>A«</p>
 9651            <p>ˇ»B</p>ˇ -->
 9652            <!-- <p>C«</p>
 9653            <p>ˇ»D</p>ˇ -->
 9654        "#
 9655        .unindent(),
 9656    );
 9657    cx.update_editor(|editor, window, cx| {
 9658        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9659    });
 9660    cx.assert_editor_state(
 9661        &r#"
 9662            <p>A«</p>
 9663            <p>ˇ»B</p>ˇ
 9664            <p>C«</p>
 9665            <p>ˇ»D</p>ˇ
 9666        "#
 9667        .unindent(),
 9668    );
 9669
 9670    // Toggle comments when different languages are active for different
 9671    // selections.
 9672    cx.set_state(
 9673        &r#"
 9674            ˇ<script>
 9675                ˇvar x = new Y();
 9676            ˇ</script>
 9677        "#
 9678        .unindent(),
 9679    );
 9680    cx.executor().run_until_parked();
 9681    cx.update_editor(|editor, window, cx| {
 9682        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9683    });
 9684    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9685    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9686    cx.assert_editor_state(
 9687        &r#"
 9688            <!-- ˇ<script> -->
 9689                // ˇvar x = new Y();
 9690            // ˇ</script>
 9691        "#
 9692        .unindent(),
 9693    );
 9694}
 9695
 9696#[gpui::test]
 9697fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9698    init_test(cx, |_| {});
 9699
 9700    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9701    let multibuffer = cx.new(|cx| {
 9702        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9703        multibuffer.push_excerpts(
 9704            buffer.clone(),
 9705            [
 9706                ExcerptRange {
 9707                    context: Point::new(0, 0)..Point::new(0, 4),
 9708                    primary: None,
 9709                },
 9710                ExcerptRange {
 9711                    context: Point::new(1, 0)..Point::new(1, 4),
 9712                    primary: None,
 9713                },
 9714            ],
 9715            cx,
 9716        );
 9717        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9718        multibuffer
 9719    });
 9720
 9721    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9722    editor.update_in(cx, |editor, window, cx| {
 9723        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9724        editor.change_selections(None, window, cx, |s| {
 9725            s.select_ranges([
 9726                Point::new(0, 0)..Point::new(0, 0),
 9727                Point::new(1, 0)..Point::new(1, 0),
 9728            ])
 9729        });
 9730
 9731        editor.handle_input("X", window, cx);
 9732        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9733        assert_eq!(
 9734            editor.selections.ranges(cx),
 9735            [
 9736                Point::new(0, 1)..Point::new(0, 1),
 9737                Point::new(1, 1)..Point::new(1, 1),
 9738            ]
 9739        );
 9740
 9741        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9742        editor.change_selections(None, window, cx, |s| {
 9743            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9744        });
 9745        editor.backspace(&Default::default(), window, cx);
 9746        assert_eq!(editor.text(cx), "Xa\nbbb");
 9747        assert_eq!(
 9748            editor.selections.ranges(cx),
 9749            [Point::new(1, 0)..Point::new(1, 0)]
 9750        );
 9751
 9752        editor.change_selections(None, window, cx, |s| {
 9753            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9754        });
 9755        editor.backspace(&Default::default(), window, cx);
 9756        assert_eq!(editor.text(cx), "X\nbb");
 9757        assert_eq!(
 9758            editor.selections.ranges(cx),
 9759            [Point::new(0, 1)..Point::new(0, 1)]
 9760        );
 9761    });
 9762}
 9763
 9764#[gpui::test]
 9765fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9766    init_test(cx, |_| {});
 9767
 9768    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9769    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9770        indoc! {"
 9771            [aaaa
 9772            (bbbb]
 9773            cccc)",
 9774        },
 9775        markers.clone(),
 9776    );
 9777    let excerpt_ranges = markers.into_iter().map(|marker| {
 9778        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9779        ExcerptRange {
 9780            context,
 9781            primary: None,
 9782        }
 9783    });
 9784    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9785    let multibuffer = cx.new(|cx| {
 9786        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9787        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9788        multibuffer
 9789    });
 9790
 9791    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9792    editor.update_in(cx, |editor, window, cx| {
 9793        let (expected_text, selection_ranges) = marked_text_ranges(
 9794            indoc! {"
 9795                aaaa
 9796                bˇbbb
 9797                bˇbbˇb
 9798                cccc"
 9799            },
 9800            true,
 9801        );
 9802        assert_eq!(editor.text(cx), expected_text);
 9803        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9804
 9805        editor.handle_input("X", window, cx);
 9806
 9807        let (expected_text, expected_selections) = marked_text_ranges(
 9808            indoc! {"
 9809                aaaa
 9810                bXˇbbXb
 9811                bXˇbbXˇb
 9812                cccc"
 9813            },
 9814            false,
 9815        );
 9816        assert_eq!(editor.text(cx), expected_text);
 9817        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9818
 9819        editor.newline(&Newline, window, cx);
 9820        let (expected_text, expected_selections) = marked_text_ranges(
 9821            indoc! {"
 9822                aaaa
 9823                bX
 9824                ˇbbX
 9825                b
 9826                bX
 9827                ˇbbX
 9828                ˇb
 9829                cccc"
 9830            },
 9831            false,
 9832        );
 9833        assert_eq!(editor.text(cx), expected_text);
 9834        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9835    });
 9836}
 9837
 9838#[gpui::test]
 9839fn test_refresh_selections(cx: &mut TestAppContext) {
 9840    init_test(cx, |_| {});
 9841
 9842    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9843    let mut excerpt1_id = None;
 9844    let multibuffer = cx.new(|cx| {
 9845        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9846        excerpt1_id = multibuffer
 9847            .push_excerpts(
 9848                buffer.clone(),
 9849                [
 9850                    ExcerptRange {
 9851                        context: Point::new(0, 0)..Point::new(1, 4),
 9852                        primary: None,
 9853                    },
 9854                    ExcerptRange {
 9855                        context: Point::new(1, 0)..Point::new(2, 4),
 9856                        primary: None,
 9857                    },
 9858                ],
 9859                cx,
 9860            )
 9861            .into_iter()
 9862            .next();
 9863        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9864        multibuffer
 9865    });
 9866
 9867    let editor = cx.add_window(|window, cx| {
 9868        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9869        let snapshot = editor.snapshot(window, cx);
 9870        editor.change_selections(None, window, cx, |s| {
 9871            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9872        });
 9873        editor.begin_selection(
 9874            Point::new(2, 1).to_display_point(&snapshot),
 9875            true,
 9876            1,
 9877            window,
 9878            cx,
 9879        );
 9880        assert_eq!(
 9881            editor.selections.ranges(cx),
 9882            [
 9883                Point::new(1, 3)..Point::new(1, 3),
 9884                Point::new(2, 1)..Point::new(2, 1),
 9885            ]
 9886        );
 9887        editor
 9888    });
 9889
 9890    // Refreshing selections is a no-op when excerpts haven't changed.
 9891    _ = editor.update(cx, |editor, window, cx| {
 9892        editor.change_selections(None, window, cx, |s| s.refresh());
 9893        assert_eq!(
 9894            editor.selections.ranges(cx),
 9895            [
 9896                Point::new(1, 3)..Point::new(1, 3),
 9897                Point::new(2, 1)..Point::new(2, 1),
 9898            ]
 9899        );
 9900    });
 9901
 9902    multibuffer.update(cx, |multibuffer, cx| {
 9903        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9904    });
 9905    _ = editor.update(cx, |editor, window, cx| {
 9906        // Removing an excerpt causes the first selection to become degenerate.
 9907        assert_eq!(
 9908            editor.selections.ranges(cx),
 9909            [
 9910                Point::new(0, 0)..Point::new(0, 0),
 9911                Point::new(0, 1)..Point::new(0, 1)
 9912            ]
 9913        );
 9914
 9915        // Refreshing selections will relocate the first selection to the original buffer
 9916        // location.
 9917        editor.change_selections(None, window, cx, |s| s.refresh());
 9918        assert_eq!(
 9919            editor.selections.ranges(cx),
 9920            [
 9921                Point::new(0, 1)..Point::new(0, 1),
 9922                Point::new(0, 3)..Point::new(0, 3)
 9923            ]
 9924        );
 9925        assert!(editor.selections.pending_anchor().is_some());
 9926    });
 9927}
 9928
 9929#[gpui::test]
 9930fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9931    init_test(cx, |_| {});
 9932
 9933    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9934    let mut excerpt1_id = None;
 9935    let multibuffer = cx.new(|cx| {
 9936        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9937        excerpt1_id = multibuffer
 9938            .push_excerpts(
 9939                buffer.clone(),
 9940                [
 9941                    ExcerptRange {
 9942                        context: Point::new(0, 0)..Point::new(1, 4),
 9943                        primary: None,
 9944                    },
 9945                    ExcerptRange {
 9946                        context: Point::new(1, 0)..Point::new(2, 4),
 9947                        primary: None,
 9948                    },
 9949                ],
 9950                cx,
 9951            )
 9952            .into_iter()
 9953            .next();
 9954        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9955        multibuffer
 9956    });
 9957
 9958    let editor = cx.add_window(|window, cx| {
 9959        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9960        let snapshot = editor.snapshot(window, cx);
 9961        editor.begin_selection(
 9962            Point::new(1, 3).to_display_point(&snapshot),
 9963            false,
 9964            1,
 9965            window,
 9966            cx,
 9967        );
 9968        assert_eq!(
 9969            editor.selections.ranges(cx),
 9970            [Point::new(1, 3)..Point::new(1, 3)]
 9971        );
 9972        editor
 9973    });
 9974
 9975    multibuffer.update(cx, |multibuffer, cx| {
 9976        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9977    });
 9978    _ = editor.update(cx, |editor, window, cx| {
 9979        assert_eq!(
 9980            editor.selections.ranges(cx),
 9981            [Point::new(0, 0)..Point::new(0, 0)]
 9982        );
 9983
 9984        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9985        editor.change_selections(None, window, cx, |s| s.refresh());
 9986        assert_eq!(
 9987            editor.selections.ranges(cx),
 9988            [Point::new(0, 3)..Point::new(0, 3)]
 9989        );
 9990        assert!(editor.selections.pending_anchor().is_some());
 9991    });
 9992}
 9993
 9994#[gpui::test]
 9995async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9996    init_test(cx, |_| {});
 9997
 9998    let language = Arc::new(
 9999        Language::new(
10000            LanguageConfig {
10001                brackets: BracketPairConfig {
10002                    pairs: vec![
10003                        BracketPair {
10004                            start: "{".to_string(),
10005                            end: "}".to_string(),
10006                            close: true,
10007                            surround: true,
10008                            newline: true,
10009                        },
10010                        BracketPair {
10011                            start: "/* ".to_string(),
10012                            end: " */".to_string(),
10013                            close: true,
10014                            surround: true,
10015                            newline: true,
10016                        },
10017                    ],
10018                    ..Default::default()
10019                },
10020                ..Default::default()
10021            },
10022            Some(tree_sitter_rust::LANGUAGE.into()),
10023        )
10024        .with_indents_query("")
10025        .unwrap(),
10026    );
10027
10028    let text = concat!(
10029        "{   }\n",     //
10030        "  x\n",       //
10031        "  /*   */\n", //
10032        "x\n",         //
10033        "{{} }\n",     //
10034    );
10035
10036    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10037    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10038    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10039    editor
10040        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10041        .await;
10042
10043    editor.update_in(cx, |editor, window, cx| {
10044        editor.change_selections(None, window, cx, |s| {
10045            s.select_display_ranges([
10046                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10047                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10048                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10049            ])
10050        });
10051        editor.newline(&Newline, window, cx);
10052
10053        assert_eq!(
10054            editor.buffer().read(cx).read(cx).text(),
10055            concat!(
10056                "{ \n",    // Suppress rustfmt
10057                "\n",      //
10058                "}\n",     //
10059                "  x\n",   //
10060                "  /* \n", //
10061                "  \n",    //
10062                "  */\n",  //
10063                "x\n",     //
10064                "{{} \n",  //
10065                "}\n",     //
10066            )
10067        );
10068    });
10069}
10070
10071#[gpui::test]
10072fn test_highlighted_ranges(cx: &mut TestAppContext) {
10073    init_test(cx, |_| {});
10074
10075    let editor = cx.add_window(|window, cx| {
10076        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10077        build_editor(buffer.clone(), window, cx)
10078    });
10079
10080    _ = editor.update(cx, |editor, window, cx| {
10081        struct Type1;
10082        struct Type2;
10083
10084        let buffer = editor.buffer.read(cx).snapshot(cx);
10085
10086        let anchor_range =
10087            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10088
10089        editor.highlight_background::<Type1>(
10090            &[
10091                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10092                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10093                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10094                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10095            ],
10096            |_| Hsla::red(),
10097            cx,
10098        );
10099        editor.highlight_background::<Type2>(
10100            &[
10101                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10102                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10103                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10104                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10105            ],
10106            |_| Hsla::green(),
10107            cx,
10108        );
10109
10110        let snapshot = editor.snapshot(window, cx);
10111        let mut highlighted_ranges = editor.background_highlights_in_range(
10112            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10113            &snapshot,
10114            cx.theme().colors(),
10115        );
10116        // Enforce a consistent ordering based on color without relying on the ordering of the
10117        // highlight's `TypeId` which is non-executor.
10118        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10119        assert_eq!(
10120            highlighted_ranges,
10121            &[
10122                (
10123                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10124                    Hsla::red(),
10125                ),
10126                (
10127                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10128                    Hsla::red(),
10129                ),
10130                (
10131                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10132                    Hsla::green(),
10133                ),
10134                (
10135                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10136                    Hsla::green(),
10137                ),
10138            ]
10139        );
10140        assert_eq!(
10141            editor.background_highlights_in_range(
10142                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10143                &snapshot,
10144                cx.theme().colors(),
10145            ),
10146            &[(
10147                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10148                Hsla::red(),
10149            )]
10150        );
10151    });
10152}
10153
10154#[gpui::test]
10155async fn test_following(cx: &mut gpui::TestAppContext) {
10156    init_test(cx, |_| {});
10157
10158    let fs = FakeFs::new(cx.executor());
10159    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10160
10161    let buffer = project.update(cx, |project, cx| {
10162        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10163        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10164    });
10165    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10166    let follower = cx.update(|cx| {
10167        cx.open_window(
10168            WindowOptions {
10169                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10170                    gpui::Point::new(px(0.), px(0.)),
10171                    gpui::Point::new(px(10.), px(80.)),
10172                ))),
10173                ..Default::default()
10174            },
10175            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10176        )
10177        .unwrap()
10178    });
10179
10180    let is_still_following = Rc::new(RefCell::new(true));
10181    let follower_edit_event_count = Rc::new(RefCell::new(0));
10182    let pending_update = Rc::new(RefCell::new(None));
10183    let leader_model = leader.root(cx).unwrap();
10184    let follower_model = follower.root(cx).unwrap();
10185    _ = follower.update(cx, {
10186        let update = pending_update.clone();
10187        let is_still_following = is_still_following.clone();
10188        let follower_edit_event_count = follower_edit_event_count.clone();
10189        |_, window, cx| {
10190            cx.subscribe_in(
10191                &leader_model,
10192                window,
10193                move |_, leader, event, window, cx| {
10194                    leader.read(cx).add_event_to_update_proto(
10195                        event,
10196                        &mut update.borrow_mut(),
10197                        window,
10198                        cx,
10199                    );
10200                },
10201            )
10202            .detach();
10203
10204            cx.subscribe_in(
10205                &follower_model,
10206                window,
10207                move |_, _, event: &EditorEvent, _window, _cx| {
10208                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10209                        *is_still_following.borrow_mut() = false;
10210                    }
10211
10212                    if let EditorEvent::BufferEdited = event {
10213                        *follower_edit_event_count.borrow_mut() += 1;
10214                    }
10215                },
10216            )
10217            .detach();
10218        }
10219    });
10220
10221    // Update the selections only
10222    _ = leader.update(cx, |leader, window, cx| {
10223        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10224    });
10225    follower
10226        .update(cx, |follower, window, cx| {
10227            follower.apply_update_proto(
10228                &project,
10229                pending_update.borrow_mut().take().unwrap(),
10230                window,
10231                cx,
10232            )
10233        })
10234        .unwrap()
10235        .await
10236        .unwrap();
10237    _ = follower.update(cx, |follower, _, cx| {
10238        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10239    });
10240    assert!(*is_still_following.borrow());
10241    assert_eq!(*follower_edit_event_count.borrow(), 0);
10242
10243    // Update the scroll position only
10244    _ = leader.update(cx, |leader, window, cx| {
10245        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10246    });
10247    follower
10248        .update(cx, |follower, window, cx| {
10249            follower.apply_update_proto(
10250                &project,
10251                pending_update.borrow_mut().take().unwrap(),
10252                window,
10253                cx,
10254            )
10255        })
10256        .unwrap()
10257        .await
10258        .unwrap();
10259    assert_eq!(
10260        follower
10261            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10262            .unwrap(),
10263        gpui::Point::new(1.5, 3.5)
10264    );
10265    assert!(*is_still_following.borrow());
10266    assert_eq!(*follower_edit_event_count.borrow(), 0);
10267
10268    // Update the selections and scroll position. The follower's scroll position is updated
10269    // via autoscroll, not via the leader's exact scroll position.
10270    _ = leader.update(cx, |leader, window, cx| {
10271        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10272        leader.request_autoscroll(Autoscroll::newest(), cx);
10273        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10274    });
10275    follower
10276        .update(cx, |follower, window, cx| {
10277            follower.apply_update_proto(
10278                &project,
10279                pending_update.borrow_mut().take().unwrap(),
10280                window,
10281                cx,
10282            )
10283        })
10284        .unwrap()
10285        .await
10286        .unwrap();
10287    _ = follower.update(cx, |follower, _, cx| {
10288        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10289        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10290    });
10291    assert!(*is_still_following.borrow());
10292
10293    // Creating a pending selection that precedes another selection
10294    _ = leader.update(cx, |leader, window, cx| {
10295        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10296        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10297    });
10298    follower
10299        .update(cx, |follower, window, cx| {
10300            follower.apply_update_proto(
10301                &project,
10302                pending_update.borrow_mut().take().unwrap(),
10303                window,
10304                cx,
10305            )
10306        })
10307        .unwrap()
10308        .await
10309        .unwrap();
10310    _ = follower.update(cx, |follower, _, cx| {
10311        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10312    });
10313    assert!(*is_still_following.borrow());
10314
10315    // Extend the pending selection so that it surrounds another selection
10316    _ = leader.update(cx, |leader, window, cx| {
10317        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10318    });
10319    follower
10320        .update(cx, |follower, window, cx| {
10321            follower.apply_update_proto(
10322                &project,
10323                pending_update.borrow_mut().take().unwrap(),
10324                window,
10325                cx,
10326            )
10327        })
10328        .unwrap()
10329        .await
10330        .unwrap();
10331    _ = follower.update(cx, |follower, _, cx| {
10332        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10333    });
10334
10335    // Scrolling locally breaks the follow
10336    _ = follower.update(cx, |follower, window, cx| {
10337        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10338        follower.set_scroll_anchor(
10339            ScrollAnchor {
10340                anchor: top_anchor,
10341                offset: gpui::Point::new(0.0, 0.5),
10342            },
10343            window,
10344            cx,
10345        );
10346    });
10347    assert!(!(*is_still_following.borrow()));
10348}
10349
10350#[gpui::test]
10351async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10352    init_test(cx, |_| {});
10353
10354    let fs = FakeFs::new(cx.executor());
10355    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10356    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10357    let pane = workspace
10358        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10359        .unwrap();
10360
10361    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10362
10363    let leader = pane.update_in(cx, |_, window, cx| {
10364        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10365        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10366    });
10367
10368    // Start following the editor when it has no excerpts.
10369    let mut state_message =
10370        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10371    let workspace_model = workspace.root(cx).unwrap();
10372    let follower_1 = cx
10373        .update_window(*workspace.deref(), |_, window, cx| {
10374            Editor::from_state_proto(
10375                workspace_model,
10376                ViewId {
10377                    creator: Default::default(),
10378                    id: 0,
10379                },
10380                &mut state_message,
10381                window,
10382                cx,
10383            )
10384        })
10385        .unwrap()
10386        .unwrap()
10387        .await
10388        .unwrap();
10389
10390    let update_message = Rc::new(RefCell::new(None));
10391    follower_1.update_in(cx, {
10392        let update = update_message.clone();
10393        |_, window, cx| {
10394            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10395                leader.read(cx).add_event_to_update_proto(
10396                    event,
10397                    &mut update.borrow_mut(),
10398                    window,
10399                    cx,
10400                );
10401            })
10402            .detach();
10403        }
10404    });
10405
10406    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10407        (
10408            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10409            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10410        )
10411    });
10412
10413    // Insert some excerpts.
10414    leader.update(cx, |leader, cx| {
10415        leader.buffer.update(cx, |multibuffer, cx| {
10416            let excerpt_ids = multibuffer.push_excerpts(
10417                buffer_1.clone(),
10418                [
10419                    ExcerptRange {
10420                        context: 1..6,
10421                        primary: None,
10422                    },
10423                    ExcerptRange {
10424                        context: 12..15,
10425                        primary: None,
10426                    },
10427                    ExcerptRange {
10428                        context: 0..3,
10429                        primary: None,
10430                    },
10431                ],
10432                cx,
10433            );
10434            multibuffer.insert_excerpts_after(
10435                excerpt_ids[0],
10436                buffer_2.clone(),
10437                [
10438                    ExcerptRange {
10439                        context: 8..12,
10440                        primary: None,
10441                    },
10442                    ExcerptRange {
10443                        context: 0..6,
10444                        primary: None,
10445                    },
10446                ],
10447                cx,
10448            );
10449        });
10450    });
10451
10452    // Apply the update of adding the excerpts.
10453    follower_1
10454        .update_in(cx, |follower, window, cx| {
10455            follower.apply_update_proto(
10456                &project,
10457                update_message.borrow().clone().unwrap(),
10458                window,
10459                cx,
10460            )
10461        })
10462        .await
10463        .unwrap();
10464    assert_eq!(
10465        follower_1.update(cx, |editor, cx| editor.text(cx)),
10466        leader.update(cx, |editor, cx| editor.text(cx))
10467    );
10468    update_message.borrow_mut().take();
10469
10470    // Start following separately after it already has excerpts.
10471    let mut state_message =
10472        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10473    let workspace_model = workspace.root(cx).unwrap();
10474    let follower_2 = cx
10475        .update_window(*workspace.deref(), |_, window, cx| {
10476            Editor::from_state_proto(
10477                workspace_model,
10478                ViewId {
10479                    creator: Default::default(),
10480                    id: 0,
10481                },
10482                &mut state_message,
10483                window,
10484                cx,
10485            )
10486        })
10487        .unwrap()
10488        .unwrap()
10489        .await
10490        .unwrap();
10491    assert_eq!(
10492        follower_2.update(cx, |editor, cx| editor.text(cx)),
10493        leader.update(cx, |editor, cx| editor.text(cx))
10494    );
10495
10496    // Remove some excerpts.
10497    leader.update(cx, |leader, cx| {
10498        leader.buffer.update(cx, |multibuffer, cx| {
10499            let excerpt_ids = multibuffer.excerpt_ids();
10500            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10501            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10502        });
10503    });
10504
10505    // Apply the update of removing the excerpts.
10506    follower_1
10507        .update_in(cx, |follower, window, cx| {
10508            follower.apply_update_proto(
10509                &project,
10510                update_message.borrow().clone().unwrap(),
10511                window,
10512                cx,
10513            )
10514        })
10515        .await
10516        .unwrap();
10517    follower_2
10518        .update_in(cx, |follower, window, cx| {
10519            follower.apply_update_proto(
10520                &project,
10521                update_message.borrow().clone().unwrap(),
10522                window,
10523                cx,
10524            )
10525        })
10526        .await
10527        .unwrap();
10528    update_message.borrow_mut().take();
10529    assert_eq!(
10530        follower_1.update(cx, |editor, cx| editor.text(cx)),
10531        leader.update(cx, |editor, cx| editor.text(cx))
10532    );
10533}
10534
10535#[gpui::test]
10536async fn go_to_prev_overlapping_diagnostic(
10537    executor: BackgroundExecutor,
10538    cx: &mut gpui::TestAppContext,
10539) {
10540    init_test(cx, |_| {});
10541
10542    let mut cx = EditorTestContext::new(cx).await;
10543    let lsp_store =
10544        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10545
10546    cx.set_state(indoc! {"
10547        ˇfn func(abc def: i32) -> u32 {
10548        }
10549    "});
10550
10551    cx.update(|_, cx| {
10552        lsp_store.update(cx, |lsp_store, cx| {
10553            lsp_store
10554                .update_diagnostics(
10555                    LanguageServerId(0),
10556                    lsp::PublishDiagnosticsParams {
10557                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
10558                        version: None,
10559                        diagnostics: vec![
10560                            lsp::Diagnostic {
10561                                range: lsp::Range::new(
10562                                    lsp::Position::new(0, 11),
10563                                    lsp::Position::new(0, 12),
10564                                ),
10565                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10566                                ..Default::default()
10567                            },
10568                            lsp::Diagnostic {
10569                                range: lsp::Range::new(
10570                                    lsp::Position::new(0, 12),
10571                                    lsp::Position::new(0, 15),
10572                                ),
10573                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10574                                ..Default::default()
10575                            },
10576                            lsp::Diagnostic {
10577                                range: lsp::Range::new(
10578                                    lsp::Position::new(0, 25),
10579                                    lsp::Position::new(0, 28),
10580                                ),
10581                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10582                                ..Default::default()
10583                            },
10584                        ],
10585                    },
10586                    &[],
10587                    cx,
10588                )
10589                .unwrap()
10590        });
10591    });
10592
10593    executor.run_until_parked();
10594
10595    cx.update_editor(|editor, window, cx| {
10596        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10597    });
10598
10599    cx.assert_editor_state(indoc! {"
10600        fn func(abc def: i32) -> ˇu32 {
10601        }
10602    "});
10603
10604    cx.update_editor(|editor, window, cx| {
10605        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10606    });
10607
10608    cx.assert_editor_state(indoc! {"
10609        fn func(abc ˇdef: i32) -> u32 {
10610        }
10611    "});
10612
10613    cx.update_editor(|editor, window, cx| {
10614        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10615    });
10616
10617    cx.assert_editor_state(indoc! {"
10618        fn func(abcˇ def: i32) -> u32 {
10619        }
10620    "});
10621
10622    cx.update_editor(|editor, window, cx| {
10623        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10624    });
10625
10626    cx.assert_editor_state(indoc! {"
10627        fn func(abc def: i32) -> ˇu32 {
10628        }
10629    "});
10630}
10631
10632#[gpui::test]
10633async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10634    init_test(cx, |_| {});
10635
10636    let mut cx = EditorTestContext::new(cx).await;
10637
10638    cx.set_state(indoc! {"
10639        fn func(abˇc def: i32) -> u32 {
10640        }
10641    "});
10642    let lsp_store =
10643        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10644
10645    cx.update(|_, cx| {
10646        lsp_store.update(cx, |lsp_store, cx| {
10647            lsp_store.update_diagnostics(
10648                LanguageServerId(0),
10649                lsp::PublishDiagnosticsParams {
10650                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10651                    version: None,
10652                    diagnostics: vec![lsp::Diagnostic {
10653                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10654                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10655                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10656                        ..Default::default()
10657                    }],
10658                },
10659                &[],
10660                cx,
10661            )
10662        })
10663    }).unwrap();
10664    cx.run_until_parked();
10665    cx.update_editor(|editor, window, cx| {
10666        hover_popover::hover(editor, &Default::default(), window, cx)
10667    });
10668    cx.run_until_parked();
10669    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10670}
10671
10672#[gpui::test]
10673async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10674    init_test(cx, |_| {});
10675
10676    let mut cx = EditorTestContext::new(cx).await;
10677
10678    let diff_base = r#"
10679        use some::mod;
10680
10681        const A: u32 = 42;
10682
10683        fn main() {
10684            println!("hello");
10685
10686            println!("world");
10687        }
10688        "#
10689    .unindent();
10690
10691    // Edits are modified, removed, modified, added
10692    cx.set_state(
10693        &r#"
10694        use some::modified;
10695
10696        ˇ
10697        fn main() {
10698            println!("hello there");
10699
10700            println!("around the");
10701            println!("world");
10702        }
10703        "#
10704        .unindent(),
10705    );
10706
10707    cx.set_diff_base(&diff_base);
10708    executor.run_until_parked();
10709
10710    cx.update_editor(|editor, window, cx| {
10711        //Wrap around the bottom of the buffer
10712        for _ in 0..3 {
10713            editor.go_to_next_hunk(&GoToHunk, window, cx);
10714        }
10715    });
10716
10717    cx.assert_editor_state(
10718        &r#"
10719        ˇuse some::modified;
10720
10721
10722        fn main() {
10723            println!("hello there");
10724
10725            println!("around the");
10726            println!("world");
10727        }
10728        "#
10729        .unindent(),
10730    );
10731
10732    cx.update_editor(|editor, window, cx| {
10733        //Wrap around the top of the buffer
10734        for _ in 0..2 {
10735            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10736        }
10737    });
10738
10739    cx.assert_editor_state(
10740        &r#"
10741        use some::modified;
10742
10743
10744        fn main() {
10745        ˇ    println!("hello there");
10746
10747            println!("around the");
10748            println!("world");
10749        }
10750        "#
10751        .unindent(),
10752    );
10753
10754    cx.update_editor(|editor, window, cx| {
10755        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10756    });
10757
10758    cx.assert_editor_state(
10759        &r#"
10760        use some::modified;
10761
10762        ˇ
10763        fn main() {
10764            println!("hello there");
10765
10766            println!("around the");
10767            println!("world");
10768        }
10769        "#
10770        .unindent(),
10771    );
10772
10773    cx.update_editor(|editor, window, cx| {
10774        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10775    });
10776
10777    cx.assert_editor_state(
10778        &r#"
10779        ˇuse some::modified;
10780
10781
10782        fn main() {
10783            println!("hello there");
10784
10785            println!("around the");
10786            println!("world");
10787        }
10788        "#
10789        .unindent(),
10790    );
10791
10792    cx.update_editor(|editor, window, cx| {
10793        for _ in 0..2 {
10794            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10795        }
10796    });
10797
10798    cx.assert_editor_state(
10799        &r#"
10800        use some::modified;
10801
10802
10803        fn main() {
10804        ˇ    println!("hello there");
10805
10806            println!("around the");
10807            println!("world");
10808        }
10809        "#
10810        .unindent(),
10811    );
10812
10813    cx.update_editor(|editor, window, cx| {
10814        editor.fold(&Fold, window, cx);
10815    });
10816
10817    cx.update_editor(|editor, window, cx| {
10818        editor.go_to_next_hunk(&GoToHunk, window, cx);
10819    });
10820
10821    cx.assert_editor_state(
10822        &r#"
10823        ˇuse some::modified;
10824
10825
10826        fn main() {
10827            println!("hello there");
10828
10829            println!("around the");
10830            println!("world");
10831        }
10832        "#
10833        .unindent(),
10834    );
10835}
10836
10837#[test]
10838fn test_split_words() {
10839    fn split(text: &str) -> Vec<&str> {
10840        split_words(text).collect()
10841    }
10842
10843    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10844    assert_eq!(split("hello_world"), &["hello_", "world"]);
10845    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10846    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10847    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10848    assert_eq!(split("helloworld"), &["helloworld"]);
10849
10850    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10851}
10852
10853#[gpui::test]
10854async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10855    init_test(cx, |_| {});
10856
10857    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10858    let mut assert = |before, after| {
10859        let _state_context = cx.set_state(before);
10860        cx.run_until_parked();
10861        cx.update_editor(|editor, window, cx| {
10862            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
10863        });
10864        cx.assert_editor_state(after);
10865    };
10866
10867    // Outside bracket jumps to outside of matching bracket
10868    assert("console.logˇ(var);", "console.log(var)ˇ;");
10869    assert("console.log(var)ˇ;", "console.logˇ(var);");
10870
10871    // Inside bracket jumps to inside of matching bracket
10872    assert("console.log(ˇvar);", "console.log(varˇ);");
10873    assert("console.log(varˇ);", "console.log(ˇvar);");
10874
10875    // When outside a bracket and inside, favor jumping to the inside bracket
10876    assert(
10877        "console.log('foo', [1, 2, 3]ˇ);",
10878        "console.log(ˇ'foo', [1, 2, 3]);",
10879    );
10880    assert(
10881        "console.log(ˇ'foo', [1, 2, 3]);",
10882        "console.log('foo', [1, 2, 3]ˇ);",
10883    );
10884
10885    // Bias forward if two options are equally likely
10886    assert(
10887        "let result = curried_fun()ˇ();",
10888        "let result = curried_fun()()ˇ;",
10889    );
10890
10891    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10892    assert(
10893        indoc! {"
10894            function test() {
10895                console.log('test')ˇ
10896            }"},
10897        indoc! {"
10898            function test() {
10899                console.logˇ('test')
10900            }"},
10901    );
10902}
10903
10904#[gpui::test]
10905async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10906    init_test(cx, |_| {});
10907
10908    let fs = FakeFs::new(cx.executor());
10909    fs.insert_tree(
10910        "/a",
10911        json!({
10912            "main.rs": "fn main() { let a = 5; }",
10913            "other.rs": "// Test file",
10914        }),
10915    )
10916    .await;
10917    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10918
10919    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10920    language_registry.add(Arc::new(Language::new(
10921        LanguageConfig {
10922            name: "Rust".into(),
10923            matcher: LanguageMatcher {
10924                path_suffixes: vec!["rs".to_string()],
10925                ..Default::default()
10926            },
10927            brackets: BracketPairConfig {
10928                pairs: vec![BracketPair {
10929                    start: "{".to_string(),
10930                    end: "}".to_string(),
10931                    close: true,
10932                    surround: true,
10933                    newline: true,
10934                }],
10935                disabled_scopes_by_bracket_ix: Vec::new(),
10936            },
10937            ..Default::default()
10938        },
10939        Some(tree_sitter_rust::LANGUAGE.into()),
10940    )));
10941    let mut fake_servers = language_registry.register_fake_lsp(
10942        "Rust",
10943        FakeLspAdapter {
10944            capabilities: lsp::ServerCapabilities {
10945                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10946                    first_trigger_character: "{".to_string(),
10947                    more_trigger_character: None,
10948                }),
10949                ..Default::default()
10950            },
10951            ..Default::default()
10952        },
10953    );
10954
10955    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10956
10957    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10958
10959    let worktree_id = workspace
10960        .update(cx, |workspace, _, cx| {
10961            workspace.project().update(cx, |project, cx| {
10962                project.worktrees(cx).next().unwrap().read(cx).id()
10963            })
10964        })
10965        .unwrap();
10966
10967    let buffer = project
10968        .update(cx, |project, cx| {
10969            project.open_local_buffer("/a/main.rs", cx)
10970        })
10971        .await
10972        .unwrap();
10973    let editor_handle = workspace
10974        .update(cx, |workspace, window, cx| {
10975            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
10976        })
10977        .unwrap()
10978        .await
10979        .unwrap()
10980        .downcast::<Editor>()
10981        .unwrap();
10982
10983    cx.executor().start_waiting();
10984    let fake_server = fake_servers.next().await.unwrap();
10985
10986    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10987        assert_eq!(
10988            params.text_document_position.text_document.uri,
10989            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10990        );
10991        assert_eq!(
10992            params.text_document_position.position,
10993            lsp::Position::new(0, 21),
10994        );
10995
10996        Ok(Some(vec![lsp::TextEdit {
10997            new_text: "]".to_string(),
10998            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10999        }]))
11000    });
11001
11002    editor_handle.update_in(cx, |editor, window, cx| {
11003        window.focus(&editor.focus_handle(cx));
11004        editor.change_selections(None, window, cx, |s| {
11005            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11006        });
11007        editor.handle_input("{", window, cx);
11008    });
11009
11010    cx.executor().run_until_parked();
11011
11012    buffer.update(cx, |buffer, _| {
11013        assert_eq!(
11014            buffer.text(),
11015            "fn main() { let a = {5}; }",
11016            "No extra braces from on type formatting should appear in the buffer"
11017        )
11018    });
11019}
11020
11021#[gpui::test]
11022async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11023    init_test(cx, |_| {});
11024
11025    let fs = FakeFs::new(cx.executor());
11026    fs.insert_tree(
11027        "/a",
11028        json!({
11029            "main.rs": "fn main() { let a = 5; }",
11030            "other.rs": "// Test file",
11031        }),
11032    )
11033    .await;
11034
11035    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11036
11037    let server_restarts = Arc::new(AtomicUsize::new(0));
11038    let closure_restarts = Arc::clone(&server_restarts);
11039    let language_server_name = "test language server";
11040    let language_name: LanguageName = "Rust".into();
11041
11042    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11043    language_registry.add(Arc::new(Language::new(
11044        LanguageConfig {
11045            name: language_name.clone(),
11046            matcher: LanguageMatcher {
11047                path_suffixes: vec!["rs".to_string()],
11048                ..Default::default()
11049            },
11050            ..Default::default()
11051        },
11052        Some(tree_sitter_rust::LANGUAGE.into()),
11053    )));
11054    let mut fake_servers = language_registry.register_fake_lsp(
11055        "Rust",
11056        FakeLspAdapter {
11057            name: language_server_name,
11058            initialization_options: Some(json!({
11059                "testOptionValue": true
11060            })),
11061            initializer: Some(Box::new(move |fake_server| {
11062                let task_restarts = Arc::clone(&closure_restarts);
11063                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11064                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11065                    futures::future::ready(Ok(()))
11066                });
11067            })),
11068            ..Default::default()
11069        },
11070    );
11071
11072    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11073    let _buffer = project
11074        .update(cx, |project, cx| {
11075            project.open_local_buffer_with_lsp("/a/main.rs", cx)
11076        })
11077        .await
11078        .unwrap();
11079    let _fake_server = fake_servers.next().await.unwrap();
11080    update_test_language_settings(cx, |language_settings| {
11081        language_settings.languages.insert(
11082            language_name.clone(),
11083            LanguageSettingsContent {
11084                tab_size: NonZeroU32::new(8),
11085                ..Default::default()
11086            },
11087        );
11088    });
11089    cx.executor().run_until_parked();
11090    assert_eq!(
11091        server_restarts.load(atomic::Ordering::Acquire),
11092        0,
11093        "Should not restart LSP server on an unrelated change"
11094    );
11095
11096    update_test_project_settings(cx, |project_settings| {
11097        project_settings.lsp.insert(
11098            "Some other server name".into(),
11099            LspSettings {
11100                binary: None,
11101                settings: None,
11102                initialization_options: Some(json!({
11103                    "some other init value": false
11104                })),
11105            },
11106        );
11107    });
11108    cx.executor().run_until_parked();
11109    assert_eq!(
11110        server_restarts.load(atomic::Ordering::Acquire),
11111        0,
11112        "Should not restart LSP server on an unrelated LSP settings change"
11113    );
11114    update_test_project_settings(cx, |project_settings| {
11115        project_settings.lsp.insert(
11116            language_server_name.into(),
11117            LspSettings {
11118                binary: None,
11119                settings: None,
11120                initialization_options: Some(json!({
11121                    "anotherInitValue": false
11122                })),
11123            },
11124        );
11125    });
11126    cx.executor().run_until_parked();
11127    assert_eq!(
11128        server_restarts.load(atomic::Ordering::Acquire),
11129        1,
11130        "Should restart LSP server on a related LSP settings change"
11131    );
11132
11133    update_test_project_settings(cx, |project_settings| {
11134        project_settings.lsp.insert(
11135            language_server_name.into(),
11136            LspSettings {
11137                binary: None,
11138                settings: None,
11139                initialization_options: Some(json!({
11140                    "anotherInitValue": false
11141                })),
11142            },
11143        );
11144    });
11145    cx.executor().run_until_parked();
11146    assert_eq!(
11147        server_restarts.load(atomic::Ordering::Acquire),
11148        1,
11149        "Should not restart LSP server on a related LSP settings change that is the same"
11150    );
11151
11152    update_test_project_settings(cx, |project_settings| {
11153        project_settings.lsp.insert(
11154            language_server_name.into(),
11155            LspSettings {
11156                binary: None,
11157                settings: None,
11158                initialization_options: None,
11159            },
11160        );
11161    });
11162    cx.executor().run_until_parked();
11163    assert_eq!(
11164        server_restarts.load(atomic::Ordering::Acquire),
11165        2,
11166        "Should restart LSP server on another related LSP settings change"
11167    );
11168}
11169
11170#[gpui::test]
11171async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11172    init_test(cx, |_| {});
11173
11174    let mut cx = EditorLspTestContext::new_rust(
11175        lsp::ServerCapabilities {
11176            completion_provider: Some(lsp::CompletionOptions {
11177                trigger_characters: Some(vec![".".to_string()]),
11178                resolve_provider: Some(true),
11179                ..Default::default()
11180            }),
11181            ..Default::default()
11182        },
11183        cx,
11184    )
11185    .await;
11186
11187    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11188    cx.simulate_keystroke(".");
11189    let completion_item = lsp::CompletionItem {
11190        label: "some".into(),
11191        kind: Some(lsp::CompletionItemKind::SNIPPET),
11192        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11193        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11194            kind: lsp::MarkupKind::Markdown,
11195            value: "```rust\nSome(2)\n```".to_string(),
11196        })),
11197        deprecated: Some(false),
11198        sort_text: Some("fffffff2".to_string()),
11199        filter_text: Some("some".to_string()),
11200        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11201        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11202            range: lsp::Range {
11203                start: lsp::Position {
11204                    line: 0,
11205                    character: 22,
11206                },
11207                end: lsp::Position {
11208                    line: 0,
11209                    character: 22,
11210                },
11211            },
11212            new_text: "Some(2)".to_string(),
11213        })),
11214        additional_text_edits: Some(vec![lsp::TextEdit {
11215            range: lsp::Range {
11216                start: lsp::Position {
11217                    line: 0,
11218                    character: 20,
11219                },
11220                end: lsp::Position {
11221                    line: 0,
11222                    character: 22,
11223                },
11224            },
11225            new_text: "".to_string(),
11226        }]),
11227        ..Default::default()
11228    };
11229
11230    let closure_completion_item = completion_item.clone();
11231    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11232        let task_completion_item = closure_completion_item.clone();
11233        async move {
11234            Ok(Some(lsp::CompletionResponse::Array(vec![
11235                task_completion_item,
11236            ])))
11237        }
11238    });
11239
11240    request.next().await;
11241
11242    cx.condition(|editor, _| editor.context_menu_visible())
11243        .await;
11244    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11245        editor
11246            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11247            .unwrap()
11248    });
11249    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11250
11251    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11252        let task_completion_item = completion_item.clone();
11253        async move { Ok(task_completion_item) }
11254    })
11255    .next()
11256    .await
11257    .unwrap();
11258    apply_additional_edits.await.unwrap();
11259    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11260}
11261
11262#[gpui::test]
11263async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11264    cx: &mut gpui::TestAppContext,
11265) {
11266    init_test(cx, |_| {});
11267
11268    let mut cx = EditorLspTestContext::new_rust(
11269        lsp::ServerCapabilities {
11270            completion_provider: Some(lsp::CompletionOptions {
11271                trigger_characters: Some(vec![".".to_string()]),
11272                resolve_provider: Some(true),
11273                ..Default::default()
11274            }),
11275            ..Default::default()
11276        },
11277        cx,
11278    )
11279    .await;
11280
11281    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11282    cx.simulate_keystroke(".");
11283
11284    let item1 = lsp::CompletionItem {
11285        label: "method id()".to_string(),
11286        filter_text: Some("id".to_string()),
11287        detail: None,
11288        documentation: None,
11289        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11290            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11291            new_text: ".id".to_string(),
11292        })),
11293        ..lsp::CompletionItem::default()
11294    };
11295
11296    let item2 = lsp::CompletionItem {
11297        label: "other".to_string(),
11298        filter_text: Some("other".to_string()),
11299        detail: None,
11300        documentation: None,
11301        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11302            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11303            new_text: ".other".to_string(),
11304        })),
11305        ..lsp::CompletionItem::default()
11306    };
11307
11308    let item1 = item1.clone();
11309    cx.handle_request::<lsp::request::Completion, _, _>({
11310        let item1 = item1.clone();
11311        move |_, _, _| {
11312            let item1 = item1.clone();
11313            let item2 = item2.clone();
11314            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11315        }
11316    })
11317    .next()
11318    .await;
11319
11320    cx.condition(|editor, _| editor.context_menu_visible())
11321        .await;
11322    cx.update_editor(|editor, _, _| {
11323        let context_menu = editor.context_menu.borrow_mut();
11324        let context_menu = context_menu
11325            .as_ref()
11326            .expect("Should have the context menu deployed");
11327        match context_menu {
11328            CodeContextMenu::Completions(completions_menu) => {
11329                let completions = completions_menu.completions.borrow_mut();
11330                assert_eq!(
11331                    completions
11332                        .iter()
11333                        .map(|completion| &completion.label.text)
11334                        .collect::<Vec<_>>(),
11335                    vec!["method id()", "other"]
11336                )
11337            }
11338            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11339        }
11340    });
11341
11342    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11343        let item1 = item1.clone();
11344        move |_, item_to_resolve, _| {
11345            let item1 = item1.clone();
11346            async move {
11347                if item1 == item_to_resolve {
11348                    Ok(lsp::CompletionItem {
11349                        label: "method id()".to_string(),
11350                        filter_text: Some("id".to_string()),
11351                        detail: Some("Now resolved!".to_string()),
11352                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11353                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11354                            range: lsp::Range::new(
11355                                lsp::Position::new(0, 22),
11356                                lsp::Position::new(0, 22),
11357                            ),
11358                            new_text: ".id".to_string(),
11359                        })),
11360                        ..lsp::CompletionItem::default()
11361                    })
11362                } else {
11363                    Ok(item_to_resolve)
11364                }
11365            }
11366        }
11367    })
11368    .next()
11369    .await
11370    .unwrap();
11371    cx.run_until_parked();
11372
11373    cx.update_editor(|editor, window, cx| {
11374        editor.context_menu_next(&Default::default(), window, cx);
11375    });
11376
11377    cx.update_editor(|editor, _, _| {
11378        let context_menu = editor.context_menu.borrow_mut();
11379        let context_menu = context_menu
11380            .as_ref()
11381            .expect("Should have the context menu deployed");
11382        match context_menu {
11383            CodeContextMenu::Completions(completions_menu) => {
11384                let completions = completions_menu.completions.borrow_mut();
11385                assert_eq!(
11386                    completions
11387                        .iter()
11388                        .map(|completion| &completion.label.text)
11389                        .collect::<Vec<_>>(),
11390                    vec!["method id() Now resolved!", "other"],
11391                    "Should update first completion label, but not second as the filter text did not match."
11392                );
11393            }
11394            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11395        }
11396    });
11397}
11398
11399#[gpui::test]
11400async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11401    init_test(cx, |_| {});
11402
11403    let mut cx = EditorLspTestContext::new_rust(
11404        lsp::ServerCapabilities {
11405            completion_provider: Some(lsp::CompletionOptions {
11406                trigger_characters: Some(vec![".".to_string()]),
11407                resolve_provider: Some(true),
11408                ..Default::default()
11409            }),
11410            ..Default::default()
11411        },
11412        cx,
11413    )
11414    .await;
11415
11416    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11417    cx.simulate_keystroke(".");
11418
11419    let unresolved_item_1 = lsp::CompletionItem {
11420        label: "id".to_string(),
11421        filter_text: Some("id".to_string()),
11422        detail: None,
11423        documentation: None,
11424        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11425            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11426            new_text: ".id".to_string(),
11427        })),
11428        ..lsp::CompletionItem::default()
11429    };
11430    let resolved_item_1 = lsp::CompletionItem {
11431        additional_text_edits: Some(vec![lsp::TextEdit {
11432            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11433            new_text: "!!".to_string(),
11434        }]),
11435        ..unresolved_item_1.clone()
11436    };
11437    let unresolved_item_2 = lsp::CompletionItem {
11438        label: "other".to_string(),
11439        filter_text: Some("other".to_string()),
11440        detail: None,
11441        documentation: None,
11442        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11443            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11444            new_text: ".other".to_string(),
11445        })),
11446        ..lsp::CompletionItem::default()
11447    };
11448    let resolved_item_2 = lsp::CompletionItem {
11449        additional_text_edits: Some(vec![lsp::TextEdit {
11450            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11451            new_text: "??".to_string(),
11452        }]),
11453        ..unresolved_item_2.clone()
11454    };
11455
11456    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11457    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11458    cx.lsp
11459        .server
11460        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11461            let unresolved_item_1 = unresolved_item_1.clone();
11462            let resolved_item_1 = resolved_item_1.clone();
11463            let unresolved_item_2 = unresolved_item_2.clone();
11464            let resolved_item_2 = resolved_item_2.clone();
11465            let resolve_requests_1 = resolve_requests_1.clone();
11466            let resolve_requests_2 = resolve_requests_2.clone();
11467            move |unresolved_request, _| {
11468                let unresolved_item_1 = unresolved_item_1.clone();
11469                let resolved_item_1 = resolved_item_1.clone();
11470                let unresolved_item_2 = unresolved_item_2.clone();
11471                let resolved_item_2 = resolved_item_2.clone();
11472                let resolve_requests_1 = resolve_requests_1.clone();
11473                let resolve_requests_2 = resolve_requests_2.clone();
11474                async move {
11475                    if unresolved_request == unresolved_item_1 {
11476                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11477                        Ok(resolved_item_1.clone())
11478                    } else if unresolved_request == unresolved_item_2 {
11479                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11480                        Ok(resolved_item_2.clone())
11481                    } else {
11482                        panic!("Unexpected completion item {unresolved_request:?}")
11483                    }
11484                }
11485            }
11486        })
11487        .detach();
11488
11489    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11490        let unresolved_item_1 = unresolved_item_1.clone();
11491        let unresolved_item_2 = unresolved_item_2.clone();
11492        async move {
11493            Ok(Some(lsp::CompletionResponse::Array(vec![
11494                unresolved_item_1,
11495                unresolved_item_2,
11496            ])))
11497        }
11498    })
11499    .next()
11500    .await;
11501
11502    cx.condition(|editor, _| editor.context_menu_visible())
11503        .await;
11504    cx.update_editor(|editor, _, _| {
11505        let context_menu = editor.context_menu.borrow_mut();
11506        let context_menu = context_menu
11507            .as_ref()
11508            .expect("Should have the context menu deployed");
11509        match context_menu {
11510            CodeContextMenu::Completions(completions_menu) => {
11511                let completions = completions_menu.completions.borrow_mut();
11512                assert_eq!(
11513                    completions
11514                        .iter()
11515                        .map(|completion| &completion.label.text)
11516                        .collect::<Vec<_>>(),
11517                    vec!["id", "other"]
11518                )
11519            }
11520            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11521        }
11522    });
11523    cx.run_until_parked();
11524
11525    cx.update_editor(|editor, window, cx| {
11526        editor.context_menu_next(&ContextMenuNext, window, cx);
11527    });
11528    cx.run_until_parked();
11529    cx.update_editor(|editor, window, cx| {
11530        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11531    });
11532    cx.run_until_parked();
11533    cx.update_editor(|editor, window, cx| {
11534        editor.context_menu_next(&ContextMenuNext, window, cx);
11535    });
11536    cx.run_until_parked();
11537    cx.update_editor(|editor, window, cx| {
11538        editor
11539            .compose_completion(&ComposeCompletion::default(), window, cx)
11540            .expect("No task returned")
11541    })
11542    .await
11543    .expect("Completion failed");
11544    cx.run_until_parked();
11545
11546    cx.update_editor(|editor, _, cx| {
11547        assert_eq!(
11548            resolve_requests_1.load(atomic::Ordering::Acquire),
11549            1,
11550            "Should always resolve once despite multiple selections"
11551        );
11552        assert_eq!(
11553            resolve_requests_2.load(atomic::Ordering::Acquire),
11554            1,
11555            "Should always resolve once after multiple selections and applying the completion"
11556        );
11557        assert_eq!(
11558            editor.text(cx),
11559            "fn main() { let a = ??.other; }",
11560            "Should use resolved data when applying the completion"
11561        );
11562    });
11563}
11564
11565#[gpui::test]
11566async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11567    init_test(cx, |_| {});
11568
11569    let item_0 = lsp::CompletionItem {
11570        label: "abs".into(),
11571        insert_text: Some("abs".into()),
11572        data: Some(json!({ "very": "special"})),
11573        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11574        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11575            lsp::InsertReplaceEdit {
11576                new_text: "abs".to_string(),
11577                insert: lsp::Range::default(),
11578                replace: lsp::Range::default(),
11579            },
11580        )),
11581        ..lsp::CompletionItem::default()
11582    };
11583    let items = iter::once(item_0.clone())
11584        .chain((11..51).map(|i| lsp::CompletionItem {
11585            label: format!("item_{}", i),
11586            insert_text: Some(format!("item_{}", i)),
11587            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11588            ..lsp::CompletionItem::default()
11589        }))
11590        .collect::<Vec<_>>();
11591
11592    let default_commit_characters = vec!["?".to_string()];
11593    let default_data = json!({ "default": "data"});
11594    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11595    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11596    let default_edit_range = lsp::Range {
11597        start: lsp::Position {
11598            line: 0,
11599            character: 5,
11600        },
11601        end: lsp::Position {
11602            line: 0,
11603            character: 5,
11604        },
11605    };
11606
11607    let item_0_out = lsp::CompletionItem {
11608        commit_characters: Some(default_commit_characters.clone()),
11609        insert_text_format: Some(default_insert_text_format),
11610        ..item_0
11611    };
11612    let items_out = iter::once(item_0_out)
11613        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11614            commit_characters: Some(default_commit_characters.clone()),
11615            data: Some(default_data.clone()),
11616            insert_text_mode: Some(default_insert_text_mode),
11617            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11618                range: default_edit_range,
11619                new_text: item.label.clone(),
11620            })),
11621            ..item.clone()
11622        }))
11623        .collect::<Vec<lsp::CompletionItem>>();
11624
11625    let mut cx = EditorLspTestContext::new_rust(
11626        lsp::ServerCapabilities {
11627            completion_provider: Some(lsp::CompletionOptions {
11628                trigger_characters: Some(vec![".".to_string()]),
11629                resolve_provider: Some(true),
11630                ..Default::default()
11631            }),
11632            ..Default::default()
11633        },
11634        cx,
11635    )
11636    .await;
11637
11638    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11639    cx.simulate_keystroke(".");
11640
11641    let completion_data = default_data.clone();
11642    let completion_characters = default_commit_characters.clone();
11643    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11644        let default_data = completion_data.clone();
11645        let default_commit_characters = completion_characters.clone();
11646        let items = items.clone();
11647        async move {
11648            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11649                items,
11650                item_defaults: Some(lsp::CompletionListItemDefaults {
11651                    data: Some(default_data.clone()),
11652                    commit_characters: Some(default_commit_characters.clone()),
11653                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11654                        default_edit_range,
11655                    )),
11656                    insert_text_format: Some(default_insert_text_format),
11657                    insert_text_mode: Some(default_insert_text_mode),
11658                }),
11659                ..lsp::CompletionList::default()
11660            })))
11661        }
11662    })
11663    .next()
11664    .await;
11665
11666    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11667    cx.lsp
11668        .server
11669        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11670            let closure_resolved_items = resolved_items.clone();
11671            move |item_to_resolve, _| {
11672                let closure_resolved_items = closure_resolved_items.clone();
11673                async move {
11674                    closure_resolved_items.lock().push(item_to_resolve.clone());
11675                    Ok(item_to_resolve)
11676                }
11677            }
11678        })
11679        .detach();
11680
11681    cx.condition(|editor, _| editor.context_menu_visible())
11682        .await;
11683    cx.run_until_parked();
11684    cx.update_editor(|editor, _, _| {
11685        let menu = editor.context_menu.borrow_mut();
11686        match menu.as_ref().expect("should have the completions menu") {
11687            CodeContextMenu::Completions(completions_menu) => {
11688                assert_eq!(
11689                    completions_menu
11690                        .entries
11691                        .borrow()
11692                        .iter()
11693                        .flat_map(|c| match c {
11694                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11695                            _ => None,
11696                        })
11697                        .collect::<Vec<String>>(),
11698                    items_out
11699                        .iter()
11700                        .map(|completion| completion.label.clone())
11701                        .collect::<Vec<String>>()
11702                );
11703            }
11704            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11705        }
11706    });
11707    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11708    // with 4 from the end.
11709    assert_eq!(
11710        *resolved_items.lock(),
11711        [
11712            &items_out[0..16],
11713            &items_out[items_out.len() - 4..items_out.len()]
11714        ]
11715        .concat()
11716        .iter()
11717        .cloned()
11718        .collect::<Vec<lsp::CompletionItem>>()
11719    );
11720    resolved_items.lock().clear();
11721
11722    cx.update_editor(|editor, window, cx| {
11723        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11724    });
11725    cx.run_until_parked();
11726    // Completions that have already been resolved are skipped.
11727    assert_eq!(
11728        *resolved_items.lock(),
11729        items_out[items_out.len() - 16..items_out.len() - 4]
11730            .iter()
11731            .cloned()
11732            .collect::<Vec<lsp::CompletionItem>>()
11733    );
11734    resolved_items.lock().clear();
11735}
11736
11737#[gpui::test]
11738async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11739    init_test(cx, |_| {});
11740
11741    let mut cx = EditorLspTestContext::new(
11742        Language::new(
11743            LanguageConfig {
11744                matcher: LanguageMatcher {
11745                    path_suffixes: vec!["jsx".into()],
11746                    ..Default::default()
11747                },
11748                overrides: [(
11749                    "element".into(),
11750                    LanguageConfigOverride {
11751                        word_characters: Override::Set(['-'].into_iter().collect()),
11752                        ..Default::default()
11753                    },
11754                )]
11755                .into_iter()
11756                .collect(),
11757                ..Default::default()
11758            },
11759            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11760        )
11761        .with_override_query("(jsx_self_closing_element) @element")
11762        .unwrap(),
11763        lsp::ServerCapabilities {
11764            completion_provider: Some(lsp::CompletionOptions {
11765                trigger_characters: Some(vec![":".to_string()]),
11766                ..Default::default()
11767            }),
11768            ..Default::default()
11769        },
11770        cx,
11771    )
11772    .await;
11773
11774    cx.lsp
11775        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11776            Ok(Some(lsp::CompletionResponse::Array(vec![
11777                lsp::CompletionItem {
11778                    label: "bg-blue".into(),
11779                    ..Default::default()
11780                },
11781                lsp::CompletionItem {
11782                    label: "bg-red".into(),
11783                    ..Default::default()
11784                },
11785                lsp::CompletionItem {
11786                    label: "bg-yellow".into(),
11787                    ..Default::default()
11788                },
11789            ])))
11790        });
11791
11792    cx.set_state(r#"<p class="bgˇ" />"#);
11793
11794    // Trigger completion when typing a dash, because the dash is an extra
11795    // word character in the 'element' scope, which contains the cursor.
11796    cx.simulate_keystroke("-");
11797    cx.executor().run_until_parked();
11798    cx.update_editor(|editor, _, _| {
11799        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11800        {
11801            assert_eq!(
11802                completion_menu_entries(&menu),
11803                &["bg-red", "bg-blue", "bg-yellow"]
11804            );
11805        } else {
11806            panic!("expected completion menu to be open");
11807        }
11808    });
11809
11810    cx.simulate_keystroke("l");
11811    cx.executor().run_until_parked();
11812    cx.update_editor(|editor, _, _| {
11813        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11814        {
11815            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11816        } else {
11817            panic!("expected completion menu to be open");
11818        }
11819    });
11820
11821    // When filtering completions, consider the character after the '-' to
11822    // be the start of a subword.
11823    cx.set_state(r#"<p class="yelˇ" />"#);
11824    cx.simulate_keystroke("l");
11825    cx.executor().run_until_parked();
11826    cx.update_editor(|editor, _, _| {
11827        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11828        {
11829            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11830        } else {
11831            panic!("expected completion menu to be open");
11832        }
11833    });
11834}
11835
11836fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11837    let entries = menu.entries.borrow();
11838    entries
11839        .iter()
11840        .flat_map(|e| match e {
11841            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11842            _ => None,
11843        })
11844        .collect()
11845}
11846
11847#[gpui::test]
11848async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11849    init_test(cx, |settings| {
11850        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11851            FormatterList(vec![Formatter::Prettier].into()),
11852        ))
11853    });
11854
11855    let fs = FakeFs::new(cx.executor());
11856    fs.insert_file("/file.ts", Default::default()).await;
11857
11858    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11859    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11860
11861    language_registry.add(Arc::new(Language::new(
11862        LanguageConfig {
11863            name: "TypeScript".into(),
11864            matcher: LanguageMatcher {
11865                path_suffixes: vec!["ts".to_string()],
11866                ..Default::default()
11867            },
11868            ..Default::default()
11869        },
11870        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11871    )));
11872    update_test_language_settings(cx, |settings| {
11873        settings.defaults.prettier = Some(PrettierSettings {
11874            allowed: true,
11875            ..PrettierSettings::default()
11876        });
11877    });
11878
11879    let test_plugin = "test_plugin";
11880    let _ = language_registry.register_fake_lsp(
11881        "TypeScript",
11882        FakeLspAdapter {
11883            prettier_plugins: vec![test_plugin],
11884            ..Default::default()
11885        },
11886    );
11887
11888    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11889    let buffer = project
11890        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11891        .await
11892        .unwrap();
11893
11894    let buffer_text = "one\ntwo\nthree\n";
11895    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11896    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11897    editor.update_in(cx, |editor, window, cx| {
11898        editor.set_text(buffer_text, window, cx)
11899    });
11900
11901    editor
11902        .update_in(cx, |editor, window, cx| {
11903            editor.perform_format(
11904                project.clone(),
11905                FormatTrigger::Manual,
11906                FormatTarget::Buffers,
11907                window,
11908                cx,
11909            )
11910        })
11911        .unwrap()
11912        .await;
11913    assert_eq!(
11914        editor.update(cx, |editor, cx| editor.text(cx)),
11915        buffer_text.to_string() + prettier_format_suffix,
11916        "Test prettier formatting was not applied to the original buffer text",
11917    );
11918
11919    update_test_language_settings(cx, |settings| {
11920        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11921    });
11922    let format = editor.update_in(cx, |editor, window, cx| {
11923        editor.perform_format(
11924            project.clone(),
11925            FormatTrigger::Manual,
11926            FormatTarget::Buffers,
11927            window,
11928            cx,
11929        )
11930    });
11931    format.await.unwrap();
11932    assert_eq!(
11933        editor.update(cx, |editor, cx| editor.text(cx)),
11934        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11935        "Autoformatting (via test prettier) was not applied to the original buffer text",
11936    );
11937}
11938
11939#[gpui::test]
11940async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11941    init_test(cx, |_| {});
11942    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11943    let base_text = indoc! {r#"
11944        struct Row;
11945        struct Row1;
11946        struct Row2;
11947
11948        struct Row4;
11949        struct Row5;
11950        struct Row6;
11951
11952        struct Row8;
11953        struct Row9;
11954        struct Row10;"#};
11955
11956    // When addition hunks are not adjacent to carets, no hunk revert is performed
11957    assert_hunk_revert(
11958        indoc! {r#"struct Row;
11959                   struct Row1;
11960                   struct Row1.1;
11961                   struct Row1.2;
11962                   struct Row2;ˇ
11963
11964                   struct Row4;
11965                   struct Row5;
11966                   struct Row6;
11967
11968                   struct Row8;
11969                   ˇstruct Row9;
11970                   struct Row9.1;
11971                   struct Row9.2;
11972                   struct Row9.3;
11973                   struct Row10;"#},
11974        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11975        indoc! {r#"struct Row;
11976                   struct Row1;
11977                   struct Row1.1;
11978                   struct Row1.2;
11979                   struct Row2;ˇ
11980
11981                   struct Row4;
11982                   struct Row5;
11983                   struct Row6;
11984
11985                   struct Row8;
11986                   ˇstruct Row9;
11987                   struct Row9.1;
11988                   struct Row9.2;
11989                   struct Row9.3;
11990                   struct Row10;"#},
11991        base_text,
11992        &mut cx,
11993    );
11994    // Same for selections
11995    assert_hunk_revert(
11996        indoc! {r#"struct Row;
11997                   struct Row1;
11998                   struct Row2;
11999                   struct Row2.1;
12000                   struct Row2.2;
12001                   «ˇ
12002                   struct Row4;
12003                   struct» Row5;
12004                   «struct Row6;
12005                   ˇ»
12006                   struct Row9.1;
12007                   struct Row9.2;
12008                   struct Row9.3;
12009                   struct Row8;
12010                   struct Row9;
12011                   struct Row10;"#},
12012        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12013        indoc! {r#"struct Row;
12014                   struct Row1;
12015                   struct Row2;
12016                   struct Row2.1;
12017                   struct Row2.2;
12018                   «ˇ
12019                   struct Row4;
12020                   struct» Row5;
12021                   «struct Row6;
12022                   ˇ»
12023                   struct Row9.1;
12024                   struct Row9.2;
12025                   struct Row9.3;
12026                   struct Row8;
12027                   struct Row9;
12028                   struct Row10;"#},
12029        base_text,
12030        &mut cx,
12031    );
12032
12033    // When carets and selections intersect the addition hunks, those are reverted.
12034    // Adjacent carets got merged.
12035    assert_hunk_revert(
12036        indoc! {r#"struct Row;
12037                   ˇ// something on the top
12038                   struct Row1;
12039                   struct Row2;
12040                   struct Roˇw3.1;
12041                   struct Row2.2;
12042                   struct Row2.3;ˇ
12043
12044                   struct Row4;
12045                   struct ˇRow5.1;
12046                   struct Row5.2;
12047                   struct «Rowˇ»5.3;
12048                   struct Row5;
12049                   struct Row6;
12050                   ˇ
12051                   struct Row9.1;
12052                   struct «Rowˇ»9.2;
12053                   struct «ˇRow»9.3;
12054                   struct Row8;
12055                   struct Row9;
12056                   «ˇ// something on bottom»
12057                   struct Row10;"#},
12058        vec![
12059            DiffHunkStatus::Added,
12060            DiffHunkStatus::Added,
12061            DiffHunkStatus::Added,
12062            DiffHunkStatus::Added,
12063            DiffHunkStatus::Added,
12064        ],
12065        indoc! {r#"struct Row;
12066                   ˇstruct Row1;
12067                   struct Row2;
12068                   ˇ
12069                   struct Row4;
12070                   ˇstruct Row5;
12071                   struct Row6;
12072                   ˇ
12073                   ˇstruct Row8;
12074                   struct Row9;
12075                   ˇstruct Row10;"#},
12076        base_text,
12077        &mut cx,
12078    );
12079}
12080
12081#[gpui::test]
12082async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12083    init_test(cx, |_| {});
12084    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12085    let base_text = indoc! {r#"
12086        struct Row;
12087        struct Row1;
12088        struct Row2;
12089
12090        struct Row4;
12091        struct Row5;
12092        struct Row6;
12093
12094        struct Row8;
12095        struct Row9;
12096        struct Row10;"#};
12097
12098    // Modification hunks behave the same as the addition ones.
12099    assert_hunk_revert(
12100        indoc! {r#"struct Row;
12101                   struct Row1;
12102                   struct Row33;
12103                   ˇ
12104                   struct Row4;
12105                   struct Row5;
12106                   struct Row6;
12107                   ˇ
12108                   struct Row99;
12109                   struct Row9;
12110                   struct Row10;"#},
12111        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12112        indoc! {r#"struct Row;
12113                   struct Row1;
12114                   struct Row33;
12115                   ˇ
12116                   struct Row4;
12117                   struct Row5;
12118                   struct Row6;
12119                   ˇ
12120                   struct Row99;
12121                   struct Row9;
12122                   struct Row10;"#},
12123        base_text,
12124        &mut cx,
12125    );
12126    assert_hunk_revert(
12127        indoc! {r#"struct Row;
12128                   struct Row1;
12129                   struct Row33;
12130                   «ˇ
12131                   struct Row4;
12132                   struct» Row5;
12133                   «struct Row6;
12134                   ˇ»
12135                   struct Row99;
12136                   struct Row9;
12137                   struct Row10;"#},
12138        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12139        indoc! {r#"struct Row;
12140                   struct Row1;
12141                   struct Row33;
12142                   «ˇ
12143                   struct Row4;
12144                   struct» Row5;
12145                   «struct Row6;
12146                   ˇ»
12147                   struct Row99;
12148                   struct Row9;
12149                   struct Row10;"#},
12150        base_text,
12151        &mut cx,
12152    );
12153
12154    assert_hunk_revert(
12155        indoc! {r#"ˇstruct Row1.1;
12156                   struct Row1;
12157                   «ˇstr»uct Row22;
12158
12159                   struct ˇRow44;
12160                   struct Row5;
12161                   struct «Rˇ»ow66;ˇ
12162
12163                   «struˇ»ct Row88;
12164                   struct Row9;
12165                   struct Row1011;ˇ"#},
12166        vec![
12167            DiffHunkStatus::Modified,
12168            DiffHunkStatus::Modified,
12169            DiffHunkStatus::Modified,
12170            DiffHunkStatus::Modified,
12171            DiffHunkStatus::Modified,
12172            DiffHunkStatus::Modified,
12173        ],
12174        indoc! {r#"struct Row;
12175                   ˇstruct Row1;
12176                   struct Row2;
12177                   ˇ
12178                   struct Row4;
12179                   ˇstruct Row5;
12180                   struct Row6;
12181                   ˇ
12182                   struct Row8;
12183                   ˇstruct Row9;
12184                   struct Row10;ˇ"#},
12185        base_text,
12186        &mut cx,
12187    );
12188}
12189
12190#[gpui::test]
12191async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12192    init_test(cx, |_| {});
12193    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12194    let base_text = indoc! {r#"
12195        one
12196
12197        two
12198        three
12199        "#};
12200
12201    cx.set_diff_base(base_text);
12202    cx.set_state("\nˇ\n");
12203    cx.executor().run_until_parked();
12204    cx.update_editor(|editor, _window, cx| {
12205        editor.expand_selected_diff_hunks(cx);
12206    });
12207    cx.executor().run_until_parked();
12208    cx.update_editor(|editor, window, cx| {
12209        editor.backspace(&Default::default(), window, cx);
12210    });
12211    cx.run_until_parked();
12212    cx.assert_state_with_diff(
12213        indoc! {r#"
12214
12215        - two
12216        - threeˇ
12217        +
12218        "#}
12219        .to_string(),
12220    );
12221}
12222
12223#[gpui::test]
12224async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12225    init_test(cx, |_| {});
12226    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12227    let base_text = indoc! {r#"struct Row;
12228struct Row1;
12229struct Row2;
12230
12231struct Row4;
12232struct Row5;
12233struct Row6;
12234
12235struct Row8;
12236struct Row9;
12237struct Row10;"#};
12238
12239    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12240    assert_hunk_revert(
12241        indoc! {r#"struct Row;
12242                   struct Row2;
12243
12244                   ˇstruct Row4;
12245                   struct Row5;
12246                   struct Row6;
12247                   ˇ
12248                   struct Row8;
12249                   struct Row10;"#},
12250        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12251        indoc! {r#"struct Row;
12252                   struct Row2;
12253
12254                   ˇstruct Row4;
12255                   struct Row5;
12256                   struct Row6;
12257                   ˇ
12258                   struct Row8;
12259                   struct Row10;"#},
12260        base_text,
12261        &mut cx,
12262    );
12263    assert_hunk_revert(
12264        indoc! {r#"struct Row;
12265                   struct Row2;
12266
12267                   «ˇstruct Row4;
12268                   struct» Row5;
12269                   «struct Row6;
12270                   ˇ»
12271                   struct Row8;
12272                   struct Row10;"#},
12273        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12274        indoc! {r#"struct Row;
12275                   struct Row2;
12276
12277                   «ˇstruct Row4;
12278                   struct» Row5;
12279                   «struct Row6;
12280                   ˇ»
12281                   struct Row8;
12282                   struct Row10;"#},
12283        base_text,
12284        &mut cx,
12285    );
12286
12287    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12288    assert_hunk_revert(
12289        indoc! {r#"struct Row;
12290                   ˇstruct Row2;
12291
12292                   struct Row4;
12293                   struct Row5;
12294                   struct Row6;
12295
12296                   struct Row8;ˇ
12297                   struct Row10;"#},
12298        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12299        indoc! {r#"struct Row;
12300                   struct Row1;
12301                   ˇstruct Row2;
12302
12303                   struct Row4;
12304                   struct Row5;
12305                   struct Row6;
12306
12307                   struct Row8;ˇ
12308                   struct Row9;
12309                   struct Row10;"#},
12310        base_text,
12311        &mut cx,
12312    );
12313    assert_hunk_revert(
12314        indoc! {r#"struct Row;
12315                   struct Row2«ˇ;
12316                   struct Row4;
12317                   struct» Row5;
12318                   «struct Row6;
12319
12320                   struct Row8;ˇ»
12321                   struct Row10;"#},
12322        vec![
12323            DiffHunkStatus::Removed,
12324            DiffHunkStatus::Removed,
12325            DiffHunkStatus::Removed,
12326        ],
12327        indoc! {r#"struct Row;
12328                   struct Row1;
12329                   struct Row2«ˇ;
12330
12331                   struct Row4;
12332                   struct» Row5;
12333                   «struct Row6;
12334
12335                   struct Row8;ˇ»
12336                   struct Row9;
12337                   struct Row10;"#},
12338        base_text,
12339        &mut cx,
12340    );
12341}
12342
12343#[gpui::test]
12344async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12345    init_test(cx, |_| {});
12346
12347    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12348    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12349    let base_text_3 =
12350        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12351
12352    let text_1 = edit_first_char_of_every_line(base_text_1);
12353    let text_2 = edit_first_char_of_every_line(base_text_2);
12354    let text_3 = edit_first_char_of_every_line(base_text_3);
12355
12356    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12357    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12358    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12359
12360    let multibuffer = cx.new(|cx| {
12361        let mut multibuffer = MultiBuffer::new(ReadWrite);
12362        multibuffer.push_excerpts(
12363            buffer_1.clone(),
12364            [
12365                ExcerptRange {
12366                    context: Point::new(0, 0)..Point::new(3, 0),
12367                    primary: None,
12368                },
12369                ExcerptRange {
12370                    context: Point::new(5, 0)..Point::new(7, 0),
12371                    primary: None,
12372                },
12373                ExcerptRange {
12374                    context: Point::new(9, 0)..Point::new(10, 4),
12375                    primary: None,
12376                },
12377            ],
12378            cx,
12379        );
12380        multibuffer.push_excerpts(
12381            buffer_2.clone(),
12382            [
12383                ExcerptRange {
12384                    context: Point::new(0, 0)..Point::new(3, 0),
12385                    primary: None,
12386                },
12387                ExcerptRange {
12388                    context: Point::new(5, 0)..Point::new(7, 0),
12389                    primary: None,
12390                },
12391                ExcerptRange {
12392                    context: Point::new(9, 0)..Point::new(10, 4),
12393                    primary: None,
12394                },
12395            ],
12396            cx,
12397        );
12398        multibuffer.push_excerpts(
12399            buffer_3.clone(),
12400            [
12401                ExcerptRange {
12402                    context: Point::new(0, 0)..Point::new(3, 0),
12403                    primary: None,
12404                },
12405                ExcerptRange {
12406                    context: Point::new(5, 0)..Point::new(7, 0),
12407                    primary: None,
12408                },
12409                ExcerptRange {
12410                    context: Point::new(9, 0)..Point::new(10, 4),
12411                    primary: None,
12412                },
12413            ],
12414            cx,
12415        );
12416        multibuffer
12417    });
12418
12419    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12420    editor.update_in(cx, |editor, _window, cx| {
12421        for (buffer, diff_base) in [
12422            (buffer_1.clone(), base_text_1),
12423            (buffer_2.clone(), base_text_2),
12424            (buffer_3.clone(), base_text_3),
12425        ] {
12426            let change_set = cx
12427                .new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx));
12428            editor
12429                .buffer
12430                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12431        }
12432    });
12433    cx.executor().run_until_parked();
12434
12435    editor.update_in(cx, |editor, window, cx| {
12436        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}");
12437        editor.select_all(&SelectAll, window, cx);
12438        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12439    });
12440    cx.executor().run_until_parked();
12441
12442    // When all ranges are selected, all buffer hunks are reverted.
12443    editor.update(cx, |editor, cx| {
12444        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");
12445    });
12446    buffer_1.update(cx, |buffer, _| {
12447        assert_eq!(buffer.text(), base_text_1);
12448    });
12449    buffer_2.update(cx, |buffer, _| {
12450        assert_eq!(buffer.text(), base_text_2);
12451    });
12452    buffer_3.update(cx, |buffer, _| {
12453        assert_eq!(buffer.text(), base_text_3);
12454    });
12455
12456    editor.update_in(cx, |editor, window, cx| {
12457        editor.undo(&Default::default(), window, cx);
12458    });
12459
12460    editor.update_in(cx, |editor, window, cx| {
12461        editor.change_selections(None, window, cx, |s| {
12462            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12463        });
12464        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12465    });
12466
12467    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12468    // but not affect buffer_2 and its related excerpts.
12469    editor.update(cx, |editor, cx| {
12470        assert_eq!(
12471            editor.text(cx),
12472            "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}"
12473        );
12474    });
12475    buffer_1.update(cx, |buffer, _| {
12476        assert_eq!(buffer.text(), base_text_1);
12477    });
12478    buffer_2.update(cx, |buffer, _| {
12479        assert_eq!(
12480            buffer.text(),
12481            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12482        );
12483    });
12484    buffer_3.update(cx, |buffer, _| {
12485        assert_eq!(
12486            buffer.text(),
12487            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12488        );
12489    });
12490
12491    fn edit_first_char_of_every_line(text: &str) -> String {
12492        text.split('\n')
12493            .map(|line| format!("X{}", &line[1..]))
12494            .collect::<Vec<_>>()
12495            .join("\n")
12496    }
12497}
12498
12499#[gpui::test]
12500async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12501    init_test(cx, |_| {});
12502
12503    let cols = 4;
12504    let rows = 10;
12505    let sample_text_1 = sample_text(rows, cols, 'a');
12506    assert_eq!(
12507        sample_text_1,
12508        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12509    );
12510    let sample_text_2 = sample_text(rows, cols, 'l');
12511    assert_eq!(
12512        sample_text_2,
12513        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12514    );
12515    let sample_text_3 = sample_text(rows, cols, 'v');
12516    assert_eq!(
12517        sample_text_3,
12518        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12519    );
12520
12521    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12522    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12523    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12524
12525    let multi_buffer = cx.new(|cx| {
12526        let mut multibuffer = MultiBuffer::new(ReadWrite);
12527        multibuffer.push_excerpts(
12528            buffer_1.clone(),
12529            [
12530                ExcerptRange {
12531                    context: Point::new(0, 0)..Point::new(3, 0),
12532                    primary: None,
12533                },
12534                ExcerptRange {
12535                    context: Point::new(5, 0)..Point::new(7, 0),
12536                    primary: None,
12537                },
12538                ExcerptRange {
12539                    context: Point::new(9, 0)..Point::new(10, 4),
12540                    primary: None,
12541                },
12542            ],
12543            cx,
12544        );
12545        multibuffer.push_excerpts(
12546            buffer_2.clone(),
12547            [
12548                ExcerptRange {
12549                    context: Point::new(0, 0)..Point::new(3, 0),
12550                    primary: None,
12551                },
12552                ExcerptRange {
12553                    context: Point::new(5, 0)..Point::new(7, 0),
12554                    primary: None,
12555                },
12556                ExcerptRange {
12557                    context: Point::new(9, 0)..Point::new(10, 4),
12558                    primary: None,
12559                },
12560            ],
12561            cx,
12562        );
12563        multibuffer.push_excerpts(
12564            buffer_3.clone(),
12565            [
12566                ExcerptRange {
12567                    context: Point::new(0, 0)..Point::new(3, 0),
12568                    primary: None,
12569                },
12570                ExcerptRange {
12571                    context: Point::new(5, 0)..Point::new(7, 0),
12572                    primary: None,
12573                },
12574                ExcerptRange {
12575                    context: Point::new(9, 0)..Point::new(10, 4),
12576                    primary: None,
12577                },
12578            ],
12579            cx,
12580        );
12581        multibuffer
12582    });
12583
12584    let fs = FakeFs::new(cx.executor());
12585    fs.insert_tree(
12586        "/a",
12587        json!({
12588            "main.rs": sample_text_1,
12589            "other.rs": sample_text_2,
12590            "lib.rs": sample_text_3,
12591        }),
12592    )
12593    .await;
12594    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12595    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12596    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12597    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12598        Editor::new(
12599            EditorMode::Full,
12600            multi_buffer,
12601            Some(project.clone()),
12602            true,
12603            window,
12604            cx,
12605        )
12606    });
12607    let multibuffer_item_id = workspace
12608        .update(cx, |workspace, window, cx| {
12609            assert!(
12610                workspace.active_item(cx).is_none(),
12611                "active item should be None before the first item is added"
12612            );
12613            workspace.add_item_to_active_pane(
12614                Box::new(multi_buffer_editor.clone()),
12615                None,
12616                true,
12617                window,
12618                cx,
12619            );
12620            let active_item = workspace
12621                .active_item(cx)
12622                .expect("should have an active item after adding the multi buffer");
12623            assert!(
12624                !active_item.is_singleton(cx),
12625                "A multi buffer was expected to active after adding"
12626            );
12627            active_item.item_id()
12628        })
12629        .unwrap();
12630    cx.executor().run_until_parked();
12631
12632    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12633        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12634            s.select_ranges(Some(1..2))
12635        });
12636        editor.open_excerpts(&OpenExcerpts, window, cx);
12637    });
12638    cx.executor().run_until_parked();
12639    let first_item_id = workspace
12640        .update(cx, |workspace, window, cx| {
12641            let active_item = workspace
12642                .active_item(cx)
12643                .expect("should have an active item after navigating into the 1st buffer");
12644            let first_item_id = active_item.item_id();
12645            assert_ne!(
12646                first_item_id, multibuffer_item_id,
12647                "Should navigate into the 1st buffer and activate it"
12648            );
12649            assert!(
12650                active_item.is_singleton(cx),
12651                "New active item should be a singleton buffer"
12652            );
12653            assert_eq!(
12654                active_item
12655                    .act_as::<Editor>(cx)
12656                    .expect("should have navigated into an editor for the 1st buffer")
12657                    .read(cx)
12658                    .text(cx),
12659                sample_text_1
12660            );
12661
12662            workspace
12663                .go_back(workspace.active_pane().downgrade(), window, cx)
12664                .detach_and_log_err(cx);
12665
12666            first_item_id
12667        })
12668        .unwrap();
12669    cx.executor().run_until_parked();
12670    workspace
12671        .update(cx, |workspace, _, cx| {
12672            let active_item = workspace
12673                .active_item(cx)
12674                .expect("should have an active item after navigating back");
12675            assert_eq!(
12676                active_item.item_id(),
12677                multibuffer_item_id,
12678                "Should navigate back to the multi buffer"
12679            );
12680            assert!(!active_item.is_singleton(cx));
12681        })
12682        .unwrap();
12683
12684    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12685        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12686            s.select_ranges(Some(39..40))
12687        });
12688        editor.open_excerpts(&OpenExcerpts, window, cx);
12689    });
12690    cx.executor().run_until_parked();
12691    let second_item_id = workspace
12692        .update(cx, |workspace, window, cx| {
12693            let active_item = workspace
12694                .active_item(cx)
12695                .expect("should have an active item after navigating into the 2nd buffer");
12696            let second_item_id = active_item.item_id();
12697            assert_ne!(
12698                second_item_id, multibuffer_item_id,
12699                "Should navigate away from the multibuffer"
12700            );
12701            assert_ne!(
12702                second_item_id, first_item_id,
12703                "Should navigate into the 2nd buffer and activate it"
12704            );
12705            assert!(
12706                active_item.is_singleton(cx),
12707                "New active item should be a singleton buffer"
12708            );
12709            assert_eq!(
12710                active_item
12711                    .act_as::<Editor>(cx)
12712                    .expect("should have navigated into an editor")
12713                    .read(cx)
12714                    .text(cx),
12715                sample_text_2
12716            );
12717
12718            workspace
12719                .go_back(workspace.active_pane().downgrade(), window, cx)
12720                .detach_and_log_err(cx);
12721
12722            second_item_id
12723        })
12724        .unwrap();
12725    cx.executor().run_until_parked();
12726    workspace
12727        .update(cx, |workspace, _, cx| {
12728            let active_item = workspace
12729                .active_item(cx)
12730                .expect("should have an active item after navigating back from the 2nd buffer");
12731            assert_eq!(
12732                active_item.item_id(),
12733                multibuffer_item_id,
12734                "Should navigate back from the 2nd buffer to the multi buffer"
12735            );
12736            assert!(!active_item.is_singleton(cx));
12737        })
12738        .unwrap();
12739
12740    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12741        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12742            s.select_ranges(Some(70..70))
12743        });
12744        editor.open_excerpts(&OpenExcerpts, window, cx);
12745    });
12746    cx.executor().run_until_parked();
12747    workspace
12748        .update(cx, |workspace, window, cx| {
12749            let active_item = workspace
12750                .active_item(cx)
12751                .expect("should have an active item after navigating into the 3rd buffer");
12752            let third_item_id = active_item.item_id();
12753            assert_ne!(
12754                third_item_id, multibuffer_item_id,
12755                "Should navigate into the 3rd buffer and activate it"
12756            );
12757            assert_ne!(third_item_id, first_item_id);
12758            assert_ne!(third_item_id, second_item_id);
12759            assert!(
12760                active_item.is_singleton(cx),
12761                "New active item should be a singleton buffer"
12762            );
12763            assert_eq!(
12764                active_item
12765                    .act_as::<Editor>(cx)
12766                    .expect("should have navigated into an editor")
12767                    .read(cx)
12768                    .text(cx),
12769                sample_text_3
12770            );
12771
12772            workspace
12773                .go_back(workspace.active_pane().downgrade(), window, cx)
12774                .detach_and_log_err(cx);
12775        })
12776        .unwrap();
12777    cx.executor().run_until_parked();
12778    workspace
12779        .update(cx, |workspace, _, cx| {
12780            let active_item = workspace
12781                .active_item(cx)
12782                .expect("should have an active item after navigating back from the 3rd buffer");
12783            assert_eq!(
12784                active_item.item_id(),
12785                multibuffer_item_id,
12786                "Should navigate back from the 3rd buffer to the multi buffer"
12787            );
12788            assert!(!active_item.is_singleton(cx));
12789        })
12790        .unwrap();
12791}
12792
12793#[gpui::test]
12794async fn test_toggle_selected_diff_hunks(
12795    executor: BackgroundExecutor,
12796    cx: &mut gpui::TestAppContext,
12797) {
12798    init_test(cx, |_| {});
12799
12800    let mut cx = EditorTestContext::new(cx).await;
12801
12802    let diff_base = r#"
12803        use some::mod;
12804
12805        const A: u32 = 42;
12806
12807        fn main() {
12808            println!("hello");
12809
12810            println!("world");
12811        }
12812        "#
12813    .unindent();
12814
12815    cx.set_state(
12816        &r#"
12817        use some::modified;
12818
12819        ˇ
12820        fn main() {
12821            println!("hello there");
12822
12823            println!("around the");
12824            println!("world");
12825        }
12826        "#
12827        .unindent(),
12828    );
12829
12830    cx.set_diff_base(&diff_base);
12831    executor.run_until_parked();
12832
12833    cx.update_editor(|editor, window, cx| {
12834        editor.go_to_next_hunk(&GoToHunk, window, cx);
12835        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12836    });
12837    executor.run_until_parked();
12838    cx.assert_state_with_diff(
12839        r#"
12840          use some::modified;
12841
12842
12843          fn main() {
12844        -     println!("hello");
12845        + ˇ    println!("hello there");
12846
12847              println!("around the");
12848              println!("world");
12849          }
12850        "#
12851        .unindent(),
12852    );
12853
12854    cx.update_editor(|editor, window, cx| {
12855        for _ in 0..2 {
12856            editor.go_to_next_hunk(&GoToHunk, window, cx);
12857            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12858        }
12859    });
12860    executor.run_until_parked();
12861    cx.assert_state_with_diff(
12862        r#"
12863        - use some::mod;
12864        + ˇuse some::modified;
12865
12866
12867          fn main() {
12868        -     println!("hello");
12869        +     println!("hello there");
12870
12871        +     println!("around the");
12872              println!("world");
12873          }
12874        "#
12875        .unindent(),
12876    );
12877
12878    cx.update_editor(|editor, window, cx| {
12879        editor.go_to_next_hunk(&GoToHunk, window, cx);
12880        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12881    });
12882    executor.run_until_parked();
12883    cx.assert_state_with_diff(
12884        r#"
12885        - use some::mod;
12886        + use some::modified;
12887
12888        - const A: u32 = 42;
12889          ˇ
12890          fn main() {
12891        -     println!("hello");
12892        +     println!("hello there");
12893
12894        +     println!("around the");
12895              println!("world");
12896          }
12897        "#
12898        .unindent(),
12899    );
12900
12901    cx.update_editor(|editor, window, cx| {
12902        editor.cancel(&Cancel, window, cx);
12903    });
12904
12905    cx.assert_state_with_diff(
12906        r#"
12907          use some::modified;
12908
12909          ˇ
12910          fn main() {
12911              println!("hello there");
12912
12913              println!("around the");
12914              println!("world");
12915          }
12916        "#
12917        .unindent(),
12918    );
12919}
12920
12921#[gpui::test]
12922async fn test_diff_base_change_with_expanded_diff_hunks(
12923    executor: BackgroundExecutor,
12924    cx: &mut gpui::TestAppContext,
12925) {
12926    init_test(cx, |_| {});
12927
12928    let mut cx = EditorTestContext::new(cx).await;
12929
12930    let diff_base = r#"
12931        use some::mod1;
12932        use some::mod2;
12933
12934        const A: u32 = 42;
12935        const B: u32 = 42;
12936        const C: u32 = 42;
12937
12938        fn main() {
12939            println!("hello");
12940
12941            println!("world");
12942        }
12943        "#
12944    .unindent();
12945
12946    cx.set_state(
12947        &r#"
12948        use some::mod2;
12949
12950        const A: u32 = 42;
12951        const C: u32 = 42;
12952
12953        fn main(ˇ) {
12954            //println!("hello");
12955
12956            println!("world");
12957            //
12958            //
12959        }
12960        "#
12961        .unindent(),
12962    );
12963
12964    cx.set_diff_base(&diff_base);
12965    executor.run_until_parked();
12966
12967    cx.update_editor(|editor, window, cx| {
12968        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
12969    });
12970    executor.run_until_parked();
12971    cx.assert_state_with_diff(
12972        r#"
12973        - use some::mod1;
12974          use some::mod2;
12975
12976          const A: u32 = 42;
12977        - const B: u32 = 42;
12978          const C: u32 = 42;
12979
12980          fn main(ˇ) {
12981        -     println!("hello");
12982        +     //println!("hello");
12983
12984              println!("world");
12985        +     //
12986        +     //
12987          }
12988        "#
12989        .unindent(),
12990    );
12991
12992    cx.set_diff_base("new diff base!");
12993    executor.run_until_parked();
12994    cx.assert_state_with_diff(
12995        r#"
12996          use some::mod2;
12997
12998          const A: u32 = 42;
12999          const C: u32 = 42;
13000
13001          fn main(ˇ) {
13002              //println!("hello");
13003
13004              println!("world");
13005              //
13006              //
13007          }
13008        "#
13009        .unindent(),
13010    );
13011
13012    cx.update_editor(|editor, window, cx| {
13013        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13014    });
13015    executor.run_until_parked();
13016    cx.assert_state_with_diff(
13017        r#"
13018        - new diff base!
13019        + use some::mod2;
13020        +
13021        + const A: u32 = 42;
13022        + const C: u32 = 42;
13023        +
13024        + fn main(ˇ) {
13025        +     //println!("hello");
13026        +
13027        +     println!("world");
13028        +     //
13029        +     //
13030        + }
13031        "#
13032        .unindent(),
13033    );
13034}
13035
13036#[gpui::test]
13037async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13038    init_test(cx, |_| {});
13039
13040    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13041    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13042    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13043    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13044    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13045    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13046
13047    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13048    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13049    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13050
13051    let multi_buffer = cx.new(|cx| {
13052        let mut multibuffer = MultiBuffer::new(ReadWrite);
13053        multibuffer.push_excerpts(
13054            buffer_1.clone(),
13055            [
13056                ExcerptRange {
13057                    context: Point::new(0, 0)..Point::new(3, 0),
13058                    primary: None,
13059                },
13060                ExcerptRange {
13061                    context: Point::new(5, 0)..Point::new(7, 0),
13062                    primary: None,
13063                },
13064                ExcerptRange {
13065                    context: Point::new(9, 0)..Point::new(10, 3),
13066                    primary: None,
13067                },
13068            ],
13069            cx,
13070        );
13071        multibuffer.push_excerpts(
13072            buffer_2.clone(),
13073            [
13074                ExcerptRange {
13075                    context: Point::new(0, 0)..Point::new(3, 0),
13076                    primary: None,
13077                },
13078                ExcerptRange {
13079                    context: Point::new(5, 0)..Point::new(7, 0),
13080                    primary: None,
13081                },
13082                ExcerptRange {
13083                    context: Point::new(9, 0)..Point::new(10, 3),
13084                    primary: None,
13085                },
13086            ],
13087            cx,
13088        );
13089        multibuffer.push_excerpts(
13090            buffer_3.clone(),
13091            [
13092                ExcerptRange {
13093                    context: Point::new(0, 0)..Point::new(3, 0),
13094                    primary: None,
13095                },
13096                ExcerptRange {
13097                    context: Point::new(5, 0)..Point::new(7, 0),
13098                    primary: None,
13099                },
13100                ExcerptRange {
13101                    context: Point::new(9, 0)..Point::new(10, 3),
13102                    primary: None,
13103                },
13104            ],
13105            cx,
13106        );
13107        multibuffer
13108    });
13109
13110    let editor = cx.add_window(|window, cx| {
13111        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13112    });
13113    editor
13114        .update(cx, |editor, _window, cx| {
13115            for (buffer, diff_base) in [
13116                (buffer_1.clone(), file_1_old),
13117                (buffer_2.clone(), file_2_old),
13118                (buffer_3.clone(), file_3_old),
13119            ] {
13120                let change_set = cx.new(|cx| {
13121                    BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
13122                });
13123                editor
13124                    .buffer
13125                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13126            }
13127        })
13128        .unwrap();
13129
13130    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13131    cx.run_until_parked();
13132
13133    cx.assert_editor_state(
13134        &"
13135            ˇaaa
13136            ccc
13137            ddd
13138
13139            ggg
13140            hhh
13141
13142
13143            lll
13144            mmm
13145            NNN
13146
13147            qqq
13148            rrr
13149
13150            uuu
13151            111
13152            222
13153            333
13154
13155            666
13156            777
13157
13158            000
13159            !!!"
13160        .unindent(),
13161    );
13162
13163    cx.update_editor(|editor, window, cx| {
13164        editor.select_all(&SelectAll, window, cx);
13165        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13166    });
13167    cx.executor().run_until_parked();
13168
13169    cx.assert_state_with_diff(
13170        "
13171            «aaa
13172          - bbb
13173            ccc
13174            ddd
13175
13176            ggg
13177            hhh
13178
13179
13180            lll
13181            mmm
13182          - nnn
13183          + NNN
13184
13185            qqq
13186            rrr
13187
13188            uuu
13189            111
13190            222
13191            333
13192
13193          + 666
13194            777
13195
13196            000
13197            !!!ˇ»"
13198            .unindent(),
13199    );
13200}
13201
13202#[gpui::test]
13203async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13204    init_test(cx, |_| {});
13205
13206    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13207    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
13208
13209    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13210    let multi_buffer = cx.new(|cx| {
13211        let mut multibuffer = MultiBuffer::new(ReadWrite);
13212        multibuffer.push_excerpts(
13213            buffer.clone(),
13214            [
13215                ExcerptRange {
13216                    context: Point::new(0, 0)..Point::new(2, 0),
13217                    primary: None,
13218                },
13219                ExcerptRange {
13220                    context: Point::new(5, 0)..Point::new(7, 0),
13221                    primary: None,
13222                },
13223            ],
13224            cx,
13225        );
13226        multibuffer
13227    });
13228
13229    let editor = cx.add_window(|window, cx| {
13230        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13231    });
13232    editor
13233        .update(cx, |editor, _window, cx| {
13234            let change_set =
13235                cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
13236            editor
13237                .buffer
13238                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13239        })
13240        .unwrap();
13241
13242    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13243    cx.run_until_parked();
13244
13245    cx.update_editor(|editor, window, cx| {
13246        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13247    });
13248    cx.executor().run_until_parked();
13249
13250    cx.assert_state_with_diff(
13251        "
13252            ˇaaa
13253          - bbb
13254          + BBB
13255
13256          + EEE
13257            fff
13258        "
13259        .unindent(),
13260    );
13261}
13262
13263#[gpui::test]
13264async fn test_edits_around_expanded_insertion_hunks(
13265    executor: BackgroundExecutor,
13266    cx: &mut gpui::TestAppContext,
13267) {
13268    init_test(cx, |_| {});
13269
13270    let mut cx = EditorTestContext::new(cx).await;
13271
13272    let diff_base = r#"
13273        use some::mod1;
13274        use some::mod2;
13275
13276        const A: u32 = 42;
13277
13278        fn main() {
13279            println!("hello");
13280
13281            println!("world");
13282        }
13283        "#
13284    .unindent();
13285    executor.run_until_parked();
13286    cx.set_state(
13287        &r#"
13288        use some::mod1;
13289        use some::mod2;
13290
13291        const A: u32 = 42;
13292        const B: u32 = 42;
13293        const C: u32 = 42;
13294        ˇ
13295
13296        fn main() {
13297            println!("hello");
13298
13299            println!("world");
13300        }
13301        "#
13302        .unindent(),
13303    );
13304
13305    cx.set_diff_base(&diff_base);
13306    executor.run_until_parked();
13307
13308    cx.update_editor(|editor, window, cx| {
13309        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13310    });
13311    executor.run_until_parked();
13312
13313    cx.assert_state_with_diff(
13314        r#"
13315        use some::mod1;
13316        use some::mod2;
13317
13318        const A: u32 = 42;
13319      + const B: u32 = 42;
13320      + const C: u32 = 42;
13321      + ˇ
13322
13323        fn main() {
13324            println!("hello");
13325
13326            println!("world");
13327        }
13328      "#
13329        .unindent(),
13330    );
13331
13332    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13333    executor.run_until_parked();
13334
13335    cx.assert_state_with_diff(
13336        r#"
13337        use some::mod1;
13338        use some::mod2;
13339
13340        const A: u32 = 42;
13341      + const B: u32 = 42;
13342      + const C: u32 = 42;
13343      + const D: u32 = 42;
13344      + ˇ
13345
13346        fn main() {
13347            println!("hello");
13348
13349            println!("world");
13350        }
13351      "#
13352        .unindent(),
13353    );
13354
13355    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13356    executor.run_until_parked();
13357
13358    cx.assert_state_with_diff(
13359        r#"
13360        use some::mod1;
13361        use some::mod2;
13362
13363        const A: u32 = 42;
13364      + const B: u32 = 42;
13365      + const C: u32 = 42;
13366      + const D: u32 = 42;
13367      + const E: u32 = 42;
13368      + ˇ
13369
13370        fn main() {
13371            println!("hello");
13372
13373            println!("world");
13374        }
13375      "#
13376        .unindent(),
13377    );
13378
13379    cx.update_editor(|editor, window, cx| {
13380        editor.delete_line(&DeleteLine, window, cx);
13381    });
13382    executor.run_until_parked();
13383
13384    cx.assert_state_with_diff(
13385        r#"
13386        use some::mod1;
13387        use some::mod2;
13388
13389        const A: u32 = 42;
13390      + const B: u32 = 42;
13391      + const C: u32 = 42;
13392      + const D: u32 = 42;
13393      + const E: u32 = 42;
13394        ˇ
13395        fn main() {
13396            println!("hello");
13397
13398            println!("world");
13399        }
13400      "#
13401        .unindent(),
13402    );
13403
13404    cx.update_editor(|editor, window, cx| {
13405        editor.move_up(&MoveUp, window, cx);
13406        editor.delete_line(&DeleteLine, window, cx);
13407        editor.move_up(&MoveUp, window, cx);
13408        editor.delete_line(&DeleteLine, window, cx);
13409        editor.move_up(&MoveUp, window, cx);
13410        editor.delete_line(&DeleteLine, window, cx);
13411    });
13412    executor.run_until_parked();
13413    cx.assert_state_with_diff(
13414        r#"
13415        use some::mod1;
13416        use some::mod2;
13417
13418        const A: u32 = 42;
13419      + const B: u32 = 42;
13420        ˇ
13421        fn main() {
13422            println!("hello");
13423
13424            println!("world");
13425        }
13426      "#
13427        .unindent(),
13428    );
13429
13430    cx.update_editor(|editor, window, cx| {
13431        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13432        editor.delete_line(&DeleteLine, window, cx);
13433    });
13434    executor.run_until_parked();
13435    cx.assert_state_with_diff(
13436        r#"
13437        ˇ
13438        fn main() {
13439            println!("hello");
13440
13441            println!("world");
13442        }
13443      "#
13444        .unindent(),
13445    );
13446}
13447
13448#[gpui::test]
13449async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13450    init_test(cx, |_| {});
13451
13452    let mut cx = EditorTestContext::new(cx).await;
13453    cx.set_diff_base(indoc! { "
13454        one
13455        two
13456        three
13457        four
13458        five
13459        "
13460    });
13461    cx.set_state(indoc! { "
13462        one
13463        ˇthree
13464        five
13465    "});
13466    cx.run_until_parked();
13467    cx.update_editor(|editor, window, cx| {
13468        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13469    });
13470    cx.assert_state_with_diff(
13471        indoc! { "
13472        one
13473      - two
13474        ˇthree
13475      - four
13476        five
13477    "}
13478        .to_string(),
13479    );
13480    cx.update_editor(|editor, window, cx| {
13481        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13482    });
13483
13484    cx.assert_state_with_diff(
13485        indoc! { "
13486        one
13487        ˇthree
13488        five
13489    "}
13490        .to_string(),
13491    );
13492
13493    cx.set_state(indoc! { "
13494        one
13495        TWO
13496        ˇthree
13497        four
13498        five
13499    "});
13500    cx.run_until_parked();
13501    cx.update_editor(|editor, window, cx| {
13502        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13503    });
13504
13505    cx.assert_state_with_diff(
13506        indoc! { "
13507            one
13508          - two
13509          + TWO
13510            ˇthree
13511            four
13512            five
13513        "}
13514        .to_string(),
13515    );
13516    cx.update_editor(|editor, window, cx| {
13517        editor.move_up(&Default::default(), window, cx);
13518        editor.move_up(&Default::default(), window, cx);
13519        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13520    });
13521    cx.assert_state_with_diff(
13522        indoc! { "
13523            one
13524            ˇTWO
13525            three
13526            four
13527            five
13528        "}
13529        .to_string(),
13530    );
13531}
13532
13533#[gpui::test]
13534async fn test_edits_around_expanded_deletion_hunks(
13535    executor: BackgroundExecutor,
13536    cx: &mut gpui::TestAppContext,
13537) {
13538    init_test(cx, |_| {});
13539
13540    let mut cx = EditorTestContext::new(cx).await;
13541
13542    let diff_base = r#"
13543        use some::mod1;
13544        use some::mod2;
13545
13546        const A: u32 = 42;
13547        const B: u32 = 42;
13548        const C: u32 = 42;
13549
13550
13551        fn main() {
13552            println!("hello");
13553
13554            println!("world");
13555        }
13556    "#
13557    .unindent();
13558    executor.run_until_parked();
13559    cx.set_state(
13560        &r#"
13561        use some::mod1;
13562        use some::mod2;
13563
13564        ˇconst B: u32 = 42;
13565        const C: u32 = 42;
13566
13567
13568        fn main() {
13569            println!("hello");
13570
13571            println!("world");
13572        }
13573        "#
13574        .unindent(),
13575    );
13576
13577    cx.set_diff_base(&diff_base);
13578    executor.run_until_parked();
13579
13580    cx.update_editor(|editor, window, cx| {
13581        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13582    });
13583    executor.run_until_parked();
13584
13585    cx.assert_state_with_diff(
13586        r#"
13587        use some::mod1;
13588        use some::mod2;
13589
13590      - const A: u32 = 42;
13591        ˇconst B: u32 = 42;
13592        const C: u32 = 42;
13593
13594
13595        fn main() {
13596            println!("hello");
13597
13598            println!("world");
13599        }
13600      "#
13601        .unindent(),
13602    );
13603
13604    cx.update_editor(|editor, window, cx| {
13605        editor.delete_line(&DeleteLine, window, cx);
13606    });
13607    executor.run_until_parked();
13608    cx.assert_state_with_diff(
13609        r#"
13610        use some::mod1;
13611        use some::mod2;
13612
13613      - const A: u32 = 42;
13614      - const B: u32 = 42;
13615        ˇconst C: u32 = 42;
13616
13617
13618        fn main() {
13619            println!("hello");
13620
13621            println!("world");
13622        }
13623      "#
13624        .unindent(),
13625    );
13626
13627    cx.update_editor(|editor, window, cx| {
13628        editor.delete_line(&DeleteLine, window, cx);
13629    });
13630    executor.run_until_parked();
13631    cx.assert_state_with_diff(
13632        r#"
13633        use some::mod1;
13634        use some::mod2;
13635
13636      - const A: u32 = 42;
13637      - const B: u32 = 42;
13638      - const C: u32 = 42;
13639        ˇ
13640
13641        fn main() {
13642            println!("hello");
13643
13644            println!("world");
13645        }
13646      "#
13647        .unindent(),
13648    );
13649
13650    cx.update_editor(|editor, window, cx| {
13651        editor.handle_input("replacement", window, cx);
13652    });
13653    executor.run_until_parked();
13654    cx.assert_state_with_diff(
13655        r#"
13656        use some::mod1;
13657        use some::mod2;
13658
13659      - const A: u32 = 42;
13660      - const B: u32 = 42;
13661      - const C: u32 = 42;
13662      -
13663      + replacementˇ
13664
13665        fn main() {
13666            println!("hello");
13667
13668            println!("world");
13669        }
13670      "#
13671        .unindent(),
13672    );
13673}
13674
13675#[gpui::test]
13676async fn test_backspace_after_deletion_hunk(
13677    executor: BackgroundExecutor,
13678    cx: &mut gpui::TestAppContext,
13679) {
13680    init_test(cx, |_| {});
13681
13682    let mut cx = EditorTestContext::new(cx).await;
13683
13684    let base_text = r#"
13685        one
13686        two
13687        three
13688        four
13689        five
13690    "#
13691    .unindent();
13692    executor.run_until_parked();
13693    cx.set_state(
13694        &r#"
13695        one
13696        two
13697        fˇour
13698        five
13699        "#
13700        .unindent(),
13701    );
13702
13703    cx.set_diff_base(&base_text);
13704    executor.run_until_parked();
13705
13706    cx.update_editor(|editor, window, cx| {
13707        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13708    });
13709    executor.run_until_parked();
13710
13711    cx.assert_state_with_diff(
13712        r#"
13713          one
13714          two
13715        - three
13716          fˇour
13717          five
13718        "#
13719        .unindent(),
13720    );
13721
13722    cx.update_editor(|editor, window, cx| {
13723        editor.backspace(&Backspace, window, cx);
13724        editor.backspace(&Backspace, window, cx);
13725    });
13726    executor.run_until_parked();
13727    cx.assert_state_with_diff(
13728        r#"
13729          one
13730          two
13731        - threeˇ
13732        - four
13733        + our
13734          five
13735        "#
13736        .unindent(),
13737    );
13738}
13739
13740#[gpui::test]
13741async fn test_edit_after_expanded_modification_hunk(
13742    executor: BackgroundExecutor,
13743    cx: &mut gpui::TestAppContext,
13744) {
13745    init_test(cx, |_| {});
13746
13747    let mut cx = EditorTestContext::new(cx).await;
13748
13749    let diff_base = r#"
13750        use some::mod1;
13751        use some::mod2;
13752
13753        const A: u32 = 42;
13754        const B: u32 = 42;
13755        const C: u32 = 42;
13756        const D: u32 = 42;
13757
13758
13759        fn main() {
13760            println!("hello");
13761
13762            println!("world");
13763        }"#
13764    .unindent();
13765
13766    cx.set_state(
13767        &r#"
13768        use some::mod1;
13769        use some::mod2;
13770
13771        const A: u32 = 42;
13772        const B: u32 = 42;
13773        const C: u32 = 43ˇ
13774        const D: u32 = 42;
13775
13776
13777        fn main() {
13778            println!("hello");
13779
13780            println!("world");
13781        }"#
13782        .unindent(),
13783    );
13784
13785    cx.set_diff_base(&diff_base);
13786    executor.run_until_parked();
13787    cx.update_editor(|editor, window, cx| {
13788        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13789    });
13790    executor.run_until_parked();
13791
13792    cx.assert_state_with_diff(
13793        r#"
13794        use some::mod1;
13795        use some::mod2;
13796
13797        const A: u32 = 42;
13798        const B: u32 = 42;
13799      - const C: u32 = 42;
13800      + const C: u32 = 43ˇ
13801        const D: u32 = 42;
13802
13803
13804        fn main() {
13805            println!("hello");
13806
13807            println!("world");
13808        }"#
13809        .unindent(),
13810    );
13811
13812    cx.update_editor(|editor, window, cx| {
13813        editor.handle_input("\nnew_line\n", window, cx);
13814    });
13815    executor.run_until_parked();
13816
13817    cx.assert_state_with_diff(
13818        r#"
13819        use some::mod1;
13820        use some::mod2;
13821
13822        const A: u32 = 42;
13823        const B: u32 = 42;
13824      - const C: u32 = 42;
13825      + const C: u32 = 43
13826      + new_line
13827      + ˇ
13828        const D: u32 = 42;
13829
13830
13831        fn main() {
13832            println!("hello");
13833
13834            println!("world");
13835        }"#
13836        .unindent(),
13837    );
13838}
13839
13840async fn setup_indent_guides_editor(
13841    text: &str,
13842    cx: &mut gpui::TestAppContext,
13843) -> (BufferId, EditorTestContext) {
13844    init_test(cx, |_| {});
13845
13846    let mut cx = EditorTestContext::new(cx).await;
13847
13848    let buffer_id = cx.update_editor(|editor, window, cx| {
13849        editor.set_text(text, window, cx);
13850        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13851
13852        buffer_ids[0]
13853    });
13854
13855    (buffer_id, cx)
13856}
13857
13858fn assert_indent_guides(
13859    range: Range<u32>,
13860    expected: Vec<IndentGuide>,
13861    active_indices: Option<Vec<usize>>,
13862    cx: &mut EditorTestContext,
13863) {
13864    let indent_guides = cx.update_editor(|editor, window, cx| {
13865        let snapshot = editor.snapshot(window, cx).display_snapshot;
13866        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13867            editor,
13868            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13869            true,
13870            &snapshot,
13871            cx,
13872        );
13873
13874        indent_guides.sort_by(|a, b| {
13875            a.depth.cmp(&b.depth).then(
13876                a.start_row
13877                    .cmp(&b.start_row)
13878                    .then(a.end_row.cmp(&b.end_row)),
13879            )
13880        });
13881        indent_guides
13882    });
13883
13884    if let Some(expected) = active_indices {
13885        let active_indices = cx.update_editor(|editor, window, cx| {
13886            let snapshot = editor.snapshot(window, cx).display_snapshot;
13887            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13888        });
13889
13890        assert_eq!(
13891            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13892            expected,
13893            "Active indent guide indices do not match"
13894        );
13895    }
13896
13897    assert_eq!(indent_guides, expected, "Indent guides do not match");
13898}
13899
13900fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13901    IndentGuide {
13902        buffer_id,
13903        start_row: MultiBufferRow(start_row),
13904        end_row: MultiBufferRow(end_row),
13905        depth,
13906        tab_size: 4,
13907        settings: IndentGuideSettings {
13908            enabled: true,
13909            line_width: 1,
13910            active_line_width: 1,
13911            ..Default::default()
13912        },
13913    }
13914}
13915
13916#[gpui::test]
13917async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13918    let (buffer_id, mut cx) = setup_indent_guides_editor(
13919        &"
13920    fn main() {
13921        let a = 1;
13922    }"
13923        .unindent(),
13924        cx,
13925    )
13926    .await;
13927
13928    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13929}
13930
13931#[gpui::test]
13932async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13933    let (buffer_id, mut cx) = setup_indent_guides_editor(
13934        &"
13935    fn main() {
13936        let a = 1;
13937        let b = 2;
13938    }"
13939        .unindent(),
13940        cx,
13941    )
13942    .await;
13943
13944    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13945}
13946
13947#[gpui::test]
13948async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13949    let (buffer_id, mut cx) = setup_indent_guides_editor(
13950        &"
13951    fn main() {
13952        let a = 1;
13953        if a == 3 {
13954            let b = 2;
13955        } else {
13956            let c = 3;
13957        }
13958    }"
13959        .unindent(),
13960        cx,
13961    )
13962    .await;
13963
13964    assert_indent_guides(
13965        0..8,
13966        vec![
13967            indent_guide(buffer_id, 1, 6, 0),
13968            indent_guide(buffer_id, 3, 3, 1),
13969            indent_guide(buffer_id, 5, 5, 1),
13970        ],
13971        None,
13972        &mut cx,
13973    );
13974}
13975
13976#[gpui::test]
13977async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13978    let (buffer_id, mut cx) = setup_indent_guides_editor(
13979        &"
13980    fn main() {
13981        let a = 1;
13982            let b = 2;
13983        let c = 3;
13984    }"
13985        .unindent(),
13986        cx,
13987    )
13988    .await;
13989
13990    assert_indent_guides(
13991        0..5,
13992        vec![
13993            indent_guide(buffer_id, 1, 3, 0),
13994            indent_guide(buffer_id, 2, 2, 1),
13995        ],
13996        None,
13997        &mut cx,
13998    );
13999}
14000
14001#[gpui::test]
14002async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14003    let (buffer_id, mut cx) = setup_indent_guides_editor(
14004        &"
14005        fn main() {
14006            let a = 1;
14007
14008            let c = 3;
14009        }"
14010        .unindent(),
14011        cx,
14012    )
14013    .await;
14014
14015    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14016}
14017
14018#[gpui::test]
14019async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14020    let (buffer_id, mut cx) = setup_indent_guides_editor(
14021        &"
14022        fn main() {
14023            let a = 1;
14024
14025            let c = 3;
14026
14027            if a == 3 {
14028                let b = 2;
14029            } else {
14030                let c = 3;
14031            }
14032        }"
14033        .unindent(),
14034        cx,
14035    )
14036    .await;
14037
14038    assert_indent_guides(
14039        0..11,
14040        vec![
14041            indent_guide(buffer_id, 1, 9, 0),
14042            indent_guide(buffer_id, 6, 6, 1),
14043            indent_guide(buffer_id, 8, 8, 1),
14044        ],
14045        None,
14046        &mut cx,
14047    );
14048}
14049
14050#[gpui::test]
14051async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14052    let (buffer_id, mut cx) = setup_indent_guides_editor(
14053        &"
14054        fn main() {
14055            let a = 1;
14056
14057            let c = 3;
14058
14059            if a == 3 {
14060                let b = 2;
14061            } else {
14062                let c = 3;
14063            }
14064        }"
14065        .unindent(),
14066        cx,
14067    )
14068    .await;
14069
14070    assert_indent_guides(
14071        1..11,
14072        vec![
14073            indent_guide(buffer_id, 1, 9, 0),
14074            indent_guide(buffer_id, 6, 6, 1),
14075            indent_guide(buffer_id, 8, 8, 1),
14076        ],
14077        None,
14078        &mut cx,
14079    );
14080}
14081
14082#[gpui::test]
14083async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14084    let (buffer_id, mut cx) = setup_indent_guides_editor(
14085        &"
14086        fn main() {
14087            let a = 1;
14088
14089            let c = 3;
14090
14091            if a == 3 {
14092                let b = 2;
14093            } else {
14094                let c = 3;
14095            }
14096        }"
14097        .unindent(),
14098        cx,
14099    )
14100    .await;
14101
14102    assert_indent_guides(
14103        1..10,
14104        vec![
14105            indent_guide(buffer_id, 1, 9, 0),
14106            indent_guide(buffer_id, 6, 6, 1),
14107            indent_guide(buffer_id, 8, 8, 1),
14108        ],
14109        None,
14110        &mut cx,
14111    );
14112}
14113
14114#[gpui::test]
14115async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14116    let (buffer_id, mut cx) = setup_indent_guides_editor(
14117        &"
14118        block1
14119            block2
14120                block3
14121                    block4
14122            block2
14123        block1
14124        block1"
14125            .unindent(),
14126        cx,
14127    )
14128    .await;
14129
14130    assert_indent_guides(
14131        1..10,
14132        vec![
14133            indent_guide(buffer_id, 1, 4, 0),
14134            indent_guide(buffer_id, 2, 3, 1),
14135            indent_guide(buffer_id, 3, 3, 2),
14136        ],
14137        None,
14138        &mut cx,
14139    );
14140}
14141
14142#[gpui::test]
14143async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14144    let (buffer_id, mut cx) = setup_indent_guides_editor(
14145        &"
14146        block1
14147            block2
14148                block3
14149
14150        block1
14151        block1"
14152            .unindent(),
14153        cx,
14154    )
14155    .await;
14156
14157    assert_indent_guides(
14158        0..6,
14159        vec![
14160            indent_guide(buffer_id, 1, 2, 0),
14161            indent_guide(buffer_id, 2, 2, 1),
14162        ],
14163        None,
14164        &mut cx,
14165    );
14166}
14167
14168#[gpui::test]
14169async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14170    let (buffer_id, mut cx) = setup_indent_guides_editor(
14171        &"
14172        block1
14173
14174
14175
14176            block2
14177        "
14178        .unindent(),
14179        cx,
14180    )
14181    .await;
14182
14183    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14184}
14185
14186#[gpui::test]
14187async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14188    let (buffer_id, mut cx) = setup_indent_guides_editor(
14189        &"
14190        def a:
14191        \tb = 3
14192        \tif True:
14193        \t\tc = 4
14194        \t\td = 5
14195        \tprint(b)
14196        "
14197        .unindent(),
14198        cx,
14199    )
14200    .await;
14201
14202    assert_indent_guides(
14203        0..6,
14204        vec![
14205            indent_guide(buffer_id, 1, 6, 0),
14206            indent_guide(buffer_id, 3, 4, 1),
14207        ],
14208        None,
14209        &mut cx,
14210    );
14211}
14212
14213#[gpui::test]
14214async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14215    let (buffer_id, mut cx) = setup_indent_guides_editor(
14216        &"
14217    fn main() {
14218        let a = 1;
14219    }"
14220        .unindent(),
14221        cx,
14222    )
14223    .await;
14224
14225    cx.update_editor(|editor, window, cx| {
14226        editor.change_selections(None, window, cx, |s| {
14227            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14228        });
14229    });
14230
14231    assert_indent_guides(
14232        0..3,
14233        vec![indent_guide(buffer_id, 1, 1, 0)],
14234        Some(vec![0]),
14235        &mut cx,
14236    );
14237}
14238
14239#[gpui::test]
14240async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14241    let (buffer_id, mut cx) = setup_indent_guides_editor(
14242        &"
14243    fn main() {
14244        if 1 == 2 {
14245            let a = 1;
14246        }
14247    }"
14248        .unindent(),
14249        cx,
14250    )
14251    .await;
14252
14253    cx.update_editor(|editor, window, cx| {
14254        editor.change_selections(None, window, cx, |s| {
14255            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14256        });
14257    });
14258
14259    assert_indent_guides(
14260        0..4,
14261        vec![
14262            indent_guide(buffer_id, 1, 3, 0),
14263            indent_guide(buffer_id, 2, 2, 1),
14264        ],
14265        Some(vec![1]),
14266        &mut cx,
14267    );
14268
14269    cx.update_editor(|editor, window, cx| {
14270        editor.change_selections(None, window, cx, |s| {
14271            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14272        });
14273    });
14274
14275    assert_indent_guides(
14276        0..4,
14277        vec![
14278            indent_guide(buffer_id, 1, 3, 0),
14279            indent_guide(buffer_id, 2, 2, 1),
14280        ],
14281        Some(vec![1]),
14282        &mut cx,
14283    );
14284
14285    cx.update_editor(|editor, window, cx| {
14286        editor.change_selections(None, window, cx, |s| {
14287            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14288        });
14289    });
14290
14291    assert_indent_guides(
14292        0..4,
14293        vec![
14294            indent_guide(buffer_id, 1, 3, 0),
14295            indent_guide(buffer_id, 2, 2, 1),
14296        ],
14297        Some(vec![0]),
14298        &mut cx,
14299    );
14300}
14301
14302#[gpui::test]
14303async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14304    let (buffer_id, mut cx) = setup_indent_guides_editor(
14305        &"
14306    fn main() {
14307        let a = 1;
14308
14309        let b = 2;
14310    }"
14311        .unindent(),
14312        cx,
14313    )
14314    .await;
14315
14316    cx.update_editor(|editor, window, cx| {
14317        editor.change_selections(None, window, cx, |s| {
14318            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14319        });
14320    });
14321
14322    assert_indent_guides(
14323        0..5,
14324        vec![indent_guide(buffer_id, 1, 3, 0)],
14325        Some(vec![0]),
14326        &mut cx,
14327    );
14328}
14329
14330#[gpui::test]
14331async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14332    let (buffer_id, mut cx) = setup_indent_guides_editor(
14333        &"
14334    def m:
14335        a = 1
14336        pass"
14337            .unindent(),
14338        cx,
14339    )
14340    .await;
14341
14342    cx.update_editor(|editor, window, cx| {
14343        editor.change_selections(None, window, cx, |s| {
14344            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14345        });
14346    });
14347
14348    assert_indent_guides(
14349        0..3,
14350        vec![indent_guide(buffer_id, 1, 2, 0)],
14351        Some(vec![0]),
14352        &mut cx,
14353    );
14354}
14355
14356#[gpui::test]
14357async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14358    init_test(cx, |_| {});
14359    let mut cx = EditorTestContext::new(cx).await;
14360    let text = indoc! {
14361        "
14362        impl A {
14363            fn b() {
14364                0;
14365                3;
14366                5;
14367                6;
14368                7;
14369            }
14370        }
14371        "
14372    };
14373    let base_text = indoc! {
14374        "
14375        impl A {
14376            fn b() {
14377                0;
14378                1;
14379                2;
14380                3;
14381                4;
14382            }
14383            fn c() {
14384                5;
14385                6;
14386                7;
14387            }
14388        }
14389        "
14390    };
14391
14392    cx.update_editor(|editor, window, cx| {
14393        editor.set_text(text, window, cx);
14394
14395        editor.buffer().update(cx, |multibuffer, cx| {
14396            let buffer = multibuffer.as_singleton().unwrap();
14397            let change_set = cx.new(|cx| {
14398                let mut change_set = BufferChangeSet::new(&buffer, cx);
14399                let _ =
14400                    change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
14401                change_set
14402            });
14403
14404            multibuffer.set_all_diff_hunks_expanded(cx);
14405            multibuffer.add_change_set(change_set, cx);
14406
14407            buffer.read(cx).remote_id()
14408        })
14409    });
14410    cx.run_until_parked();
14411
14412    cx.assert_state_with_diff(
14413        indoc! { "
14414          impl A {
14415              fn b() {
14416                  0;
14417        -         1;
14418        -         2;
14419                  3;
14420        -         4;
14421        -     }
14422        -     fn c() {
14423                  5;
14424                  6;
14425                  7;
14426              }
14427          }
14428          ˇ"
14429        }
14430        .to_string(),
14431    );
14432
14433    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14434        editor
14435            .snapshot(window, cx)
14436            .buffer_snapshot
14437            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14438            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14439            .collect::<Vec<_>>()
14440    });
14441    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14442    assert_eq!(
14443        actual_guides,
14444        vec![
14445            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14446            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14447            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14448        ]
14449    );
14450}
14451
14452#[gpui::test]
14453fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14454    init_test(cx, |_| {});
14455
14456    let editor = cx.add_window(|window, cx| {
14457        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14458        build_editor(buffer, window, cx)
14459    });
14460
14461    let render_args = Arc::new(Mutex::new(None));
14462    let snapshot = editor
14463        .update(cx, |editor, window, cx| {
14464            let snapshot = editor.buffer().read(cx).snapshot(cx);
14465            let range =
14466                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14467
14468            struct RenderArgs {
14469                row: MultiBufferRow,
14470                folded: bool,
14471                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14472            }
14473
14474            let crease = Crease::inline(
14475                range,
14476                FoldPlaceholder::test(),
14477                {
14478                    let toggle_callback = render_args.clone();
14479                    move |row, folded, callback, _window, _cx| {
14480                        *toggle_callback.lock() = Some(RenderArgs {
14481                            row,
14482                            folded,
14483                            callback,
14484                        });
14485                        div()
14486                    }
14487                },
14488                |_row, _folded, _window, _cx| div(),
14489            );
14490
14491            editor.insert_creases(Some(crease), cx);
14492            let snapshot = editor.snapshot(window, cx);
14493            let _div = snapshot.render_crease_toggle(
14494                MultiBufferRow(1),
14495                false,
14496                cx.entity().clone(),
14497                window,
14498                cx,
14499            );
14500            snapshot
14501        })
14502        .unwrap();
14503
14504    let render_args = render_args.lock().take().unwrap();
14505    assert_eq!(render_args.row, MultiBufferRow(1));
14506    assert!(!render_args.folded);
14507    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14508
14509    cx.update_window(*editor, |_, window, cx| {
14510        (render_args.callback)(true, window, cx)
14511    })
14512    .unwrap();
14513    let snapshot = editor
14514        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14515        .unwrap();
14516    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14517
14518    cx.update_window(*editor, |_, window, cx| {
14519        (render_args.callback)(false, window, cx)
14520    })
14521    .unwrap();
14522    let snapshot = editor
14523        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14524        .unwrap();
14525    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14526}
14527
14528#[gpui::test]
14529async fn test_input_text(cx: &mut gpui::TestAppContext) {
14530    init_test(cx, |_| {});
14531    let mut cx = EditorTestContext::new(cx).await;
14532
14533    cx.set_state(
14534        &r#"ˇone
14535        two
14536
14537        three
14538        fourˇ
14539        five
14540
14541        siˇx"#
14542            .unindent(),
14543    );
14544
14545    cx.dispatch_action(HandleInput(String::new()));
14546    cx.assert_editor_state(
14547        &r#"ˇone
14548        two
14549
14550        three
14551        fourˇ
14552        five
14553
14554        siˇx"#
14555            .unindent(),
14556    );
14557
14558    cx.dispatch_action(HandleInput("AAAA".to_string()));
14559    cx.assert_editor_state(
14560        &r#"AAAAˇone
14561        two
14562
14563        three
14564        fourAAAAˇ
14565        five
14566
14567        siAAAAˇx"#
14568            .unindent(),
14569    );
14570}
14571
14572#[gpui::test]
14573async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14574    init_test(cx, |_| {});
14575
14576    let mut cx = EditorTestContext::new(cx).await;
14577    cx.set_state(
14578        r#"let foo = 1;
14579let foo = 2;
14580let foo = 3;
14581let fooˇ = 4;
14582let foo = 5;
14583let foo = 6;
14584let foo = 7;
14585let foo = 8;
14586let foo = 9;
14587let foo = 10;
14588let foo = 11;
14589let foo = 12;
14590let foo = 13;
14591let foo = 14;
14592let foo = 15;"#,
14593    );
14594
14595    cx.update_editor(|e, window, cx| {
14596        assert_eq!(
14597            e.next_scroll_position,
14598            NextScrollCursorCenterTopBottom::Center,
14599            "Default next scroll direction is center",
14600        );
14601
14602        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14603        assert_eq!(
14604            e.next_scroll_position,
14605            NextScrollCursorCenterTopBottom::Top,
14606            "After center, next scroll direction should be top",
14607        );
14608
14609        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14610        assert_eq!(
14611            e.next_scroll_position,
14612            NextScrollCursorCenterTopBottom::Bottom,
14613            "After top, next scroll direction should be bottom",
14614        );
14615
14616        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14617        assert_eq!(
14618            e.next_scroll_position,
14619            NextScrollCursorCenterTopBottom::Center,
14620            "After bottom, scrolling should start over",
14621        );
14622
14623        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14624        assert_eq!(
14625            e.next_scroll_position,
14626            NextScrollCursorCenterTopBottom::Top,
14627            "Scrolling continues if retriggered fast enough"
14628        );
14629    });
14630
14631    cx.executor()
14632        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14633    cx.executor().run_until_parked();
14634    cx.update_editor(|e, _, _| {
14635        assert_eq!(
14636            e.next_scroll_position,
14637            NextScrollCursorCenterTopBottom::Center,
14638            "If scrolling is not triggered fast enough, it should reset"
14639        );
14640    });
14641}
14642
14643#[gpui::test]
14644async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14645    init_test(cx, |_| {});
14646    let mut cx = EditorLspTestContext::new_rust(
14647        lsp::ServerCapabilities {
14648            definition_provider: Some(lsp::OneOf::Left(true)),
14649            references_provider: Some(lsp::OneOf::Left(true)),
14650            ..lsp::ServerCapabilities::default()
14651        },
14652        cx,
14653    )
14654    .await;
14655
14656    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14657        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14658            move |params, _| async move {
14659                if empty_go_to_definition {
14660                    Ok(None)
14661                } else {
14662                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14663                        uri: params.text_document_position_params.text_document.uri,
14664                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14665                    })))
14666                }
14667            },
14668        );
14669        let references =
14670            cx.lsp
14671                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14672                    Ok(Some(vec![lsp::Location {
14673                        uri: params.text_document_position.text_document.uri,
14674                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14675                    }]))
14676                });
14677        (go_to_definition, references)
14678    };
14679
14680    cx.set_state(
14681        &r#"fn one() {
14682            let mut a = ˇtwo();
14683        }
14684
14685        fn two() {}"#
14686            .unindent(),
14687    );
14688    set_up_lsp_handlers(false, &mut cx);
14689    let navigated = cx
14690        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14691        .await
14692        .expect("Failed to navigate to definition");
14693    assert_eq!(
14694        navigated,
14695        Navigated::Yes,
14696        "Should have navigated to definition from the GetDefinition response"
14697    );
14698    cx.assert_editor_state(
14699        &r#"fn one() {
14700            let mut a = two();
14701        }
14702
14703        fn «twoˇ»() {}"#
14704            .unindent(),
14705    );
14706
14707    let editors = cx.update_workspace(|workspace, _, cx| {
14708        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14709    });
14710    cx.update_editor(|_, _, test_editor_cx| {
14711        assert_eq!(
14712            editors.len(),
14713            1,
14714            "Initially, only one, test, editor should be open in the workspace"
14715        );
14716        assert_eq!(
14717            test_editor_cx.entity(),
14718            editors.last().expect("Asserted len is 1").clone()
14719        );
14720    });
14721
14722    set_up_lsp_handlers(true, &mut cx);
14723    let navigated = cx
14724        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14725        .await
14726        .expect("Failed to navigate to lookup references");
14727    assert_eq!(
14728        navigated,
14729        Navigated::Yes,
14730        "Should have navigated to references as a fallback after empty GoToDefinition response"
14731    );
14732    // We should not change the selections in the existing file,
14733    // if opening another milti buffer with the references
14734    cx.assert_editor_state(
14735        &r#"fn one() {
14736            let mut a = two();
14737        }
14738
14739        fn «twoˇ»() {}"#
14740            .unindent(),
14741    );
14742    let editors = cx.update_workspace(|workspace, _, cx| {
14743        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14744    });
14745    cx.update_editor(|_, _, test_editor_cx| {
14746        assert_eq!(
14747            editors.len(),
14748            2,
14749            "After falling back to references search, we open a new editor with the results"
14750        );
14751        let references_fallback_text = editors
14752            .into_iter()
14753            .find(|new_editor| *new_editor != test_editor_cx.entity())
14754            .expect("Should have one non-test editor now")
14755            .read(test_editor_cx)
14756            .text(test_editor_cx);
14757        assert_eq!(
14758            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14759            "Should use the range from the references response and not the GoToDefinition one"
14760        );
14761    });
14762}
14763
14764#[gpui::test]
14765async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14766    init_test(cx, |_| {});
14767
14768    let language = Arc::new(Language::new(
14769        LanguageConfig::default(),
14770        Some(tree_sitter_rust::LANGUAGE.into()),
14771    ));
14772
14773    let text = r#"
14774        #[cfg(test)]
14775        mod tests() {
14776            #[test]
14777            fn runnable_1() {
14778                let a = 1;
14779            }
14780
14781            #[test]
14782            fn runnable_2() {
14783                let a = 1;
14784                let b = 2;
14785            }
14786        }
14787    "#
14788    .unindent();
14789
14790    let fs = FakeFs::new(cx.executor());
14791    fs.insert_file("/file.rs", Default::default()).await;
14792
14793    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14794    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14795    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14796    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14797    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14798
14799    let editor = cx.new_window_entity(|window, cx| {
14800        Editor::new(
14801            EditorMode::Full,
14802            multi_buffer,
14803            Some(project.clone()),
14804            true,
14805            window,
14806            cx,
14807        )
14808    });
14809
14810    editor.update_in(cx, |editor, window, cx| {
14811        editor.tasks.insert(
14812            (buffer.read(cx).remote_id(), 3),
14813            RunnableTasks {
14814                templates: vec![],
14815                offset: MultiBufferOffset(43),
14816                column: 0,
14817                extra_variables: HashMap::default(),
14818                context_range: BufferOffset(43)..BufferOffset(85),
14819            },
14820        );
14821        editor.tasks.insert(
14822            (buffer.read(cx).remote_id(), 8),
14823            RunnableTasks {
14824                templates: vec![],
14825                offset: MultiBufferOffset(86),
14826                column: 0,
14827                extra_variables: HashMap::default(),
14828                context_range: BufferOffset(86)..BufferOffset(191),
14829            },
14830        );
14831
14832        // Test finding task when cursor is inside function body
14833        editor.change_selections(None, window, cx, |s| {
14834            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14835        });
14836        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14837        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14838
14839        // Test finding task when cursor is on function name
14840        editor.change_selections(None, window, cx, |s| {
14841            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14842        });
14843        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14844        assert_eq!(row, 8, "Should find task when cursor is on function name");
14845    });
14846}
14847
14848#[gpui::test]
14849async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14850    init_test(cx, |_| {});
14851
14852    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14853    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14854    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14855
14856    let fs = FakeFs::new(cx.executor());
14857    fs.insert_tree(
14858        "/a",
14859        json!({
14860            "first.rs": sample_text_1,
14861            "second.rs": sample_text_2,
14862            "third.rs": sample_text_3,
14863        }),
14864    )
14865    .await;
14866    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14867    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14868    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14869    let worktree = project.update(cx, |project, cx| {
14870        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14871        assert_eq!(worktrees.len(), 1);
14872        worktrees.pop().unwrap()
14873    });
14874    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14875
14876    let buffer_1 = project
14877        .update(cx, |project, cx| {
14878            project.open_buffer((worktree_id, "first.rs"), cx)
14879        })
14880        .await
14881        .unwrap();
14882    let buffer_2 = project
14883        .update(cx, |project, cx| {
14884            project.open_buffer((worktree_id, "second.rs"), cx)
14885        })
14886        .await
14887        .unwrap();
14888    let buffer_3 = project
14889        .update(cx, |project, cx| {
14890            project.open_buffer((worktree_id, "third.rs"), cx)
14891        })
14892        .await
14893        .unwrap();
14894
14895    let multi_buffer = cx.new(|cx| {
14896        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14897        multi_buffer.push_excerpts(
14898            buffer_1.clone(),
14899            [
14900                ExcerptRange {
14901                    context: Point::new(0, 0)..Point::new(3, 0),
14902                    primary: None,
14903                },
14904                ExcerptRange {
14905                    context: Point::new(5, 0)..Point::new(7, 0),
14906                    primary: None,
14907                },
14908                ExcerptRange {
14909                    context: Point::new(9, 0)..Point::new(10, 4),
14910                    primary: None,
14911                },
14912            ],
14913            cx,
14914        );
14915        multi_buffer.push_excerpts(
14916            buffer_2.clone(),
14917            [
14918                ExcerptRange {
14919                    context: Point::new(0, 0)..Point::new(3, 0),
14920                    primary: None,
14921                },
14922                ExcerptRange {
14923                    context: Point::new(5, 0)..Point::new(7, 0),
14924                    primary: None,
14925                },
14926                ExcerptRange {
14927                    context: Point::new(9, 0)..Point::new(10, 4),
14928                    primary: None,
14929                },
14930            ],
14931            cx,
14932        );
14933        multi_buffer.push_excerpts(
14934            buffer_3.clone(),
14935            [
14936                ExcerptRange {
14937                    context: Point::new(0, 0)..Point::new(3, 0),
14938                    primary: None,
14939                },
14940                ExcerptRange {
14941                    context: Point::new(5, 0)..Point::new(7, 0),
14942                    primary: None,
14943                },
14944                ExcerptRange {
14945                    context: Point::new(9, 0)..Point::new(10, 4),
14946                    primary: None,
14947                },
14948            ],
14949            cx,
14950        );
14951        multi_buffer
14952    });
14953    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14954        Editor::new(
14955            EditorMode::Full,
14956            multi_buffer,
14957            Some(project.clone()),
14958            true,
14959            window,
14960            cx,
14961        )
14962    });
14963
14964    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
14965    assert_eq!(
14966        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14967        full_text,
14968    );
14969
14970    multi_buffer_editor.update(cx, |editor, cx| {
14971        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14972    });
14973    assert_eq!(
14974        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14975        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14976        "After folding the first buffer, its text should not be displayed"
14977    );
14978
14979    multi_buffer_editor.update(cx, |editor, cx| {
14980        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14981    });
14982    assert_eq!(
14983        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14984        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14985        "After folding the second buffer, its text should not be displayed"
14986    );
14987
14988    multi_buffer_editor.update(cx, |editor, cx| {
14989        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14990    });
14991    assert_eq!(
14992        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14993        "\n\n\n\n\n",
14994        "After folding the third buffer, its text should not be displayed"
14995    );
14996
14997    // Emulate selection inside the fold logic, that should work
14998    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14999        editor
15000            .snapshot(window, cx)
15001            .next_line_boundary(Point::new(0, 4));
15002    });
15003
15004    multi_buffer_editor.update(cx, |editor, cx| {
15005        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15006    });
15007    assert_eq!(
15008        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15009        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15010        "After unfolding the second buffer, its text should be displayed"
15011    );
15012
15013    multi_buffer_editor.update(cx, |editor, cx| {
15014        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15015    });
15016    assert_eq!(
15017        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15018        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15019        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15020    );
15021
15022    multi_buffer_editor.update(cx, |editor, cx| {
15023        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15024    });
15025    assert_eq!(
15026        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15027        full_text,
15028        "After unfolding the all buffers, all original text should be displayed"
15029    );
15030}
15031
15032#[gpui::test]
15033async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15034    init_test(cx, |_| {});
15035
15036    let sample_text_1 = "1111\n2222\n3333".to_string();
15037    let sample_text_2 = "4444\n5555\n6666".to_string();
15038    let sample_text_3 = "7777\n8888\n9999".to_string();
15039
15040    let fs = FakeFs::new(cx.executor());
15041    fs.insert_tree(
15042        "/a",
15043        json!({
15044            "first.rs": sample_text_1,
15045            "second.rs": sample_text_2,
15046            "third.rs": sample_text_3,
15047        }),
15048    )
15049    .await;
15050    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15051    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15052    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15053    let worktree = project.update(cx, |project, cx| {
15054        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15055        assert_eq!(worktrees.len(), 1);
15056        worktrees.pop().unwrap()
15057    });
15058    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15059
15060    let buffer_1 = project
15061        .update(cx, |project, cx| {
15062            project.open_buffer((worktree_id, "first.rs"), cx)
15063        })
15064        .await
15065        .unwrap();
15066    let buffer_2 = project
15067        .update(cx, |project, cx| {
15068            project.open_buffer((worktree_id, "second.rs"), cx)
15069        })
15070        .await
15071        .unwrap();
15072    let buffer_3 = project
15073        .update(cx, |project, cx| {
15074            project.open_buffer((worktree_id, "third.rs"), cx)
15075        })
15076        .await
15077        .unwrap();
15078
15079    let multi_buffer = cx.new(|cx| {
15080        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15081        multi_buffer.push_excerpts(
15082            buffer_1.clone(),
15083            [ExcerptRange {
15084                context: Point::new(0, 0)..Point::new(3, 0),
15085                primary: None,
15086            }],
15087            cx,
15088        );
15089        multi_buffer.push_excerpts(
15090            buffer_2.clone(),
15091            [ExcerptRange {
15092                context: Point::new(0, 0)..Point::new(3, 0),
15093                primary: None,
15094            }],
15095            cx,
15096        );
15097        multi_buffer.push_excerpts(
15098            buffer_3.clone(),
15099            [ExcerptRange {
15100                context: Point::new(0, 0)..Point::new(3, 0),
15101                primary: None,
15102            }],
15103            cx,
15104        );
15105        multi_buffer
15106    });
15107
15108    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15109        Editor::new(
15110            EditorMode::Full,
15111            multi_buffer,
15112            Some(project.clone()),
15113            true,
15114            window,
15115            cx,
15116        )
15117    });
15118
15119    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15120    assert_eq!(
15121        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15122        full_text,
15123    );
15124
15125    multi_buffer_editor.update(cx, |editor, cx| {
15126        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15127    });
15128    assert_eq!(
15129        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15130        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15131        "After folding the first buffer, its text should not be displayed"
15132    );
15133
15134    multi_buffer_editor.update(cx, |editor, cx| {
15135        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15136    });
15137
15138    assert_eq!(
15139        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15140        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15141        "After folding the second buffer, its text should not be displayed"
15142    );
15143
15144    multi_buffer_editor.update(cx, |editor, cx| {
15145        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15146    });
15147    assert_eq!(
15148        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15149        "\n\n\n\n\n",
15150        "After folding the third buffer, its text should not be displayed"
15151    );
15152
15153    multi_buffer_editor.update(cx, |editor, cx| {
15154        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15155    });
15156    assert_eq!(
15157        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15158        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15159        "After unfolding the second buffer, its text should be displayed"
15160    );
15161
15162    multi_buffer_editor.update(cx, |editor, cx| {
15163        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15164    });
15165    assert_eq!(
15166        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15167        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15168        "After unfolding the first buffer, its text should be displayed"
15169    );
15170
15171    multi_buffer_editor.update(cx, |editor, cx| {
15172        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15173    });
15174    assert_eq!(
15175        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15176        full_text,
15177        "After unfolding all buffers, all original text should be displayed"
15178    );
15179}
15180
15181#[gpui::test]
15182async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15183    init_test(cx, |_| {});
15184
15185    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15186
15187    let fs = FakeFs::new(cx.executor());
15188    fs.insert_tree(
15189        "/a",
15190        json!({
15191            "main.rs": sample_text,
15192        }),
15193    )
15194    .await;
15195    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15196    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15197    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15198    let worktree = project.update(cx, |project, cx| {
15199        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15200        assert_eq!(worktrees.len(), 1);
15201        worktrees.pop().unwrap()
15202    });
15203    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15204
15205    let buffer_1 = project
15206        .update(cx, |project, cx| {
15207            project.open_buffer((worktree_id, "main.rs"), cx)
15208        })
15209        .await
15210        .unwrap();
15211
15212    let multi_buffer = cx.new(|cx| {
15213        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15214        multi_buffer.push_excerpts(
15215            buffer_1.clone(),
15216            [ExcerptRange {
15217                context: Point::new(0, 0)
15218                    ..Point::new(
15219                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15220                        0,
15221                    ),
15222                primary: None,
15223            }],
15224            cx,
15225        );
15226        multi_buffer
15227    });
15228    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15229        Editor::new(
15230            EditorMode::Full,
15231            multi_buffer,
15232            Some(project.clone()),
15233            true,
15234            window,
15235            cx,
15236        )
15237    });
15238
15239    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15240    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15241        enum TestHighlight {}
15242        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15243        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15244        editor.highlight_text::<TestHighlight>(
15245            vec![highlight_range.clone()],
15246            HighlightStyle::color(Hsla::green()),
15247            cx,
15248        );
15249        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15250    });
15251
15252    let full_text = format!("\n\n\n{sample_text}\n");
15253    assert_eq!(
15254        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15255        full_text,
15256    );
15257}
15258
15259#[gpui::test]
15260async fn test_inline_completion_text(cx: &mut TestAppContext) {
15261    init_test(cx, |_| {});
15262
15263    // Simple insertion
15264    assert_highlighted_edits(
15265        "Hello, world!",
15266        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15267        true,
15268        cx,
15269        |highlighted_edits, cx| {
15270            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15271            assert_eq!(highlighted_edits.highlights.len(), 1);
15272            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15273            assert_eq!(
15274                highlighted_edits.highlights[0].1.background_color,
15275                Some(cx.theme().status().created_background)
15276            );
15277        },
15278    )
15279    .await;
15280
15281    // Replacement
15282    assert_highlighted_edits(
15283        "This is a test.",
15284        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15285        false,
15286        cx,
15287        |highlighted_edits, cx| {
15288            assert_eq!(highlighted_edits.text, "That is a test.");
15289            assert_eq!(highlighted_edits.highlights.len(), 1);
15290            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15291            assert_eq!(
15292                highlighted_edits.highlights[0].1.background_color,
15293                Some(cx.theme().status().created_background)
15294            );
15295        },
15296    )
15297    .await;
15298
15299    // Multiple edits
15300    assert_highlighted_edits(
15301        "Hello, world!",
15302        vec![
15303            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15304            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15305        ],
15306        false,
15307        cx,
15308        |highlighted_edits, cx| {
15309            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15310            assert_eq!(highlighted_edits.highlights.len(), 2);
15311            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15312            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15313            assert_eq!(
15314                highlighted_edits.highlights[0].1.background_color,
15315                Some(cx.theme().status().created_background)
15316            );
15317            assert_eq!(
15318                highlighted_edits.highlights[1].1.background_color,
15319                Some(cx.theme().status().created_background)
15320            );
15321        },
15322    )
15323    .await;
15324
15325    // Multiple lines with edits
15326    assert_highlighted_edits(
15327        "First line\nSecond line\nThird line\nFourth line",
15328        vec![
15329            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15330            (
15331                Point::new(2, 0)..Point::new(2, 10),
15332                "New third line".to_string(),
15333            ),
15334            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15335        ],
15336        false,
15337        cx,
15338        |highlighted_edits, cx| {
15339            assert_eq!(
15340                highlighted_edits.text,
15341                "Second modified\nNew third line\nFourth updated line"
15342            );
15343            assert_eq!(highlighted_edits.highlights.len(), 3);
15344            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15345            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15346            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15347            for highlight in &highlighted_edits.highlights {
15348                assert_eq!(
15349                    highlight.1.background_color,
15350                    Some(cx.theme().status().created_background)
15351                );
15352            }
15353        },
15354    )
15355    .await;
15356}
15357
15358#[gpui::test]
15359async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15360    init_test(cx, |_| {});
15361
15362    // Deletion
15363    assert_highlighted_edits(
15364        "Hello, world!",
15365        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15366        true,
15367        cx,
15368        |highlighted_edits, cx| {
15369            assert_eq!(highlighted_edits.text, "Hello, world!");
15370            assert_eq!(highlighted_edits.highlights.len(), 1);
15371            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15372            assert_eq!(
15373                highlighted_edits.highlights[0].1.background_color,
15374                Some(cx.theme().status().deleted_background)
15375            );
15376        },
15377    )
15378    .await;
15379
15380    // Insertion
15381    assert_highlighted_edits(
15382        "Hello, world!",
15383        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15384        true,
15385        cx,
15386        |highlighted_edits, cx| {
15387            assert_eq!(highlighted_edits.highlights.len(), 1);
15388            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15389            assert_eq!(
15390                highlighted_edits.highlights[0].1.background_color,
15391                Some(cx.theme().status().created_background)
15392            );
15393        },
15394    )
15395    .await;
15396}
15397
15398async fn assert_highlighted_edits(
15399    text: &str,
15400    edits: Vec<(Range<Point>, String)>,
15401    include_deletions: bool,
15402    cx: &mut TestAppContext,
15403    assertion_fn: impl Fn(HighlightedEdits, &App),
15404) {
15405    let window = cx.add_window(|window, cx| {
15406        let buffer = MultiBuffer::build_simple(text, cx);
15407        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15408    });
15409    let cx = &mut VisualTestContext::from_window(*window, cx);
15410
15411    let (buffer, snapshot) = window
15412        .update(cx, |editor, _window, cx| {
15413            (
15414                editor.buffer().clone(),
15415                editor.buffer().read(cx).snapshot(cx),
15416            )
15417        })
15418        .unwrap();
15419
15420    let edits = edits
15421        .into_iter()
15422        .map(|(range, edit)| {
15423            (
15424                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15425                edit,
15426            )
15427        })
15428        .collect::<Vec<_>>();
15429
15430    let text_anchor_edits = edits
15431        .clone()
15432        .into_iter()
15433        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15434        .collect::<Vec<_>>();
15435
15436    let edit_preview = window
15437        .update(cx, |_, _window, cx| {
15438            buffer
15439                .read(cx)
15440                .as_singleton()
15441                .unwrap()
15442                .read(cx)
15443                .preview_edits(text_anchor_edits.into(), cx)
15444        })
15445        .unwrap()
15446        .await;
15447
15448    cx.update(|_window, cx| {
15449        let highlighted_edits = inline_completion_edit_text(
15450            &snapshot.as_singleton().unwrap().2,
15451            &edits,
15452            &edit_preview,
15453            include_deletions,
15454            cx,
15455        )
15456        .expect("Missing highlighted edits");
15457        assertion_fn(highlighted_edits, cx)
15458    });
15459}
15460
15461#[gpui::test]
15462async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15463    init_test(cx, |_| {});
15464    let capabilities = lsp::ServerCapabilities {
15465        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15466            prepare_provider: Some(true),
15467            work_done_progress_options: Default::default(),
15468        })),
15469        ..Default::default()
15470    };
15471    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15472
15473    cx.set_state(indoc! {"
15474        struct Fˇoo {}
15475    "});
15476
15477    cx.update_editor(|editor, _, cx| {
15478        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15479        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15480        editor.highlight_background::<DocumentHighlightRead>(
15481            &[highlight_range],
15482            |c| c.editor_document_highlight_read_background,
15483            cx,
15484        );
15485    });
15486
15487    let mut prepare_rename_handler =
15488        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15489            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15490                start: lsp::Position {
15491                    line: 0,
15492                    character: 7,
15493                },
15494                end: lsp::Position {
15495                    line: 0,
15496                    character: 10,
15497                },
15498            })))
15499        });
15500    let prepare_rename_task = cx
15501        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15502        .expect("Prepare rename was not started");
15503    prepare_rename_handler.next().await.unwrap();
15504    prepare_rename_task.await.expect("Prepare rename failed");
15505
15506    let mut rename_handler =
15507        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15508            let edit = lsp::TextEdit {
15509                range: lsp::Range {
15510                    start: lsp::Position {
15511                        line: 0,
15512                        character: 7,
15513                    },
15514                    end: lsp::Position {
15515                        line: 0,
15516                        character: 10,
15517                    },
15518                },
15519                new_text: "FooRenamed".to_string(),
15520            };
15521            Ok(Some(lsp::WorkspaceEdit::new(
15522                // Specify the same edit twice
15523                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15524            )))
15525        });
15526    let rename_task = cx
15527        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15528        .expect("Confirm rename was not started");
15529    rename_handler.next().await.unwrap();
15530    rename_task.await.expect("Confirm rename failed");
15531    cx.run_until_parked();
15532
15533    // Despite two edits, only one is actually applied as those are identical
15534    cx.assert_editor_state(indoc! {"
15535        struct FooRenamedˇ {}
15536    "});
15537}
15538
15539#[gpui::test]
15540async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15541    init_test(cx, |_| {});
15542    // These capabilities indicate that the server does not support prepare rename.
15543    let capabilities = lsp::ServerCapabilities {
15544        rename_provider: Some(lsp::OneOf::Left(true)),
15545        ..Default::default()
15546    };
15547    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15548
15549    cx.set_state(indoc! {"
15550        struct Fˇoo {}
15551    "});
15552
15553    cx.update_editor(|editor, _window, cx| {
15554        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15555        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15556        editor.highlight_background::<DocumentHighlightRead>(
15557            &[highlight_range],
15558            |c| c.editor_document_highlight_read_background,
15559            cx,
15560        );
15561    });
15562
15563    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15564        .expect("Prepare rename was not started")
15565        .await
15566        .expect("Prepare rename failed");
15567
15568    let mut rename_handler =
15569        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15570            let edit = lsp::TextEdit {
15571                range: lsp::Range {
15572                    start: lsp::Position {
15573                        line: 0,
15574                        character: 7,
15575                    },
15576                    end: lsp::Position {
15577                        line: 0,
15578                        character: 10,
15579                    },
15580                },
15581                new_text: "FooRenamed".to_string(),
15582            };
15583            Ok(Some(lsp::WorkspaceEdit::new(
15584                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15585            )))
15586        });
15587    let rename_task = cx
15588        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15589        .expect("Confirm rename was not started");
15590    rename_handler.next().await.unwrap();
15591    rename_task.await.expect("Confirm rename failed");
15592    cx.run_until_parked();
15593
15594    // Correct range is renamed, as `surrounding_word` is used to find it.
15595    cx.assert_editor_state(indoc! {"
15596        struct FooRenamedˇ {}
15597    "});
15598}
15599
15600fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15601    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15602    point..point
15603}
15604
15605fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15606    let (text, ranges) = marked_text_ranges(marked_text, true);
15607    assert_eq!(editor.text(cx), text);
15608    assert_eq!(
15609        editor.selections.ranges(cx),
15610        ranges,
15611        "Assert selections are {}",
15612        marked_text
15613    );
15614}
15615
15616pub fn handle_signature_help_request(
15617    cx: &mut EditorLspTestContext,
15618    mocked_response: lsp::SignatureHelp,
15619) -> impl Future<Output = ()> {
15620    let mut request =
15621        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15622            let mocked_response = mocked_response.clone();
15623            async move { Ok(Some(mocked_response)) }
15624        });
15625
15626    async move {
15627        request.next().await;
15628    }
15629}
15630
15631/// Handle completion request passing a marked string specifying where the completion
15632/// should be triggered from using '|' character, what range should be replaced, and what completions
15633/// should be returned using '<' and '>' to delimit the range
15634pub fn handle_completion_request(
15635    cx: &mut EditorLspTestContext,
15636    marked_string: &str,
15637    completions: Vec<&'static str>,
15638    counter: Arc<AtomicUsize>,
15639) -> impl Future<Output = ()> {
15640    let complete_from_marker: TextRangeMarker = '|'.into();
15641    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15642    let (_, mut marked_ranges) = marked_text_ranges_by(
15643        marked_string,
15644        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15645    );
15646
15647    let complete_from_position =
15648        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15649    let replace_range =
15650        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15651
15652    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15653        let completions = completions.clone();
15654        counter.fetch_add(1, atomic::Ordering::Release);
15655        async move {
15656            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15657            assert_eq!(
15658                params.text_document_position.position,
15659                complete_from_position
15660            );
15661            Ok(Some(lsp::CompletionResponse::Array(
15662                completions
15663                    .iter()
15664                    .map(|completion_text| lsp::CompletionItem {
15665                        label: completion_text.to_string(),
15666                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15667                            range: replace_range,
15668                            new_text: completion_text.to_string(),
15669                        })),
15670                        ..Default::default()
15671                    })
15672                    .collect(),
15673            )))
15674        }
15675    });
15676
15677    async move {
15678        request.next().await;
15679    }
15680}
15681
15682fn handle_resolve_completion_request(
15683    cx: &mut EditorLspTestContext,
15684    edits: Option<Vec<(&'static str, &'static str)>>,
15685) -> impl Future<Output = ()> {
15686    let edits = edits.map(|edits| {
15687        edits
15688            .iter()
15689            .map(|(marked_string, new_text)| {
15690                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15691                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15692                lsp::TextEdit::new(replace_range, new_text.to_string())
15693            })
15694            .collect::<Vec<_>>()
15695    });
15696
15697    let mut request =
15698        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15699            let edits = edits.clone();
15700            async move {
15701                Ok(lsp::CompletionItem {
15702                    additional_text_edits: edits,
15703                    ..Default::default()
15704                })
15705            }
15706        });
15707
15708    async move {
15709        request.next().await;
15710    }
15711}
15712
15713pub(crate) fn update_test_language_settings(
15714    cx: &mut TestAppContext,
15715    f: impl Fn(&mut AllLanguageSettingsContent),
15716) {
15717    cx.update(|cx| {
15718        SettingsStore::update_global(cx, |store, cx| {
15719            store.update_user_settings::<AllLanguageSettings>(cx, f);
15720        });
15721    });
15722}
15723
15724pub(crate) fn update_test_project_settings(
15725    cx: &mut TestAppContext,
15726    f: impl Fn(&mut ProjectSettings),
15727) {
15728    cx.update(|cx| {
15729        SettingsStore::update_global(cx, |store, cx| {
15730            store.update_user_settings::<ProjectSettings>(cx, f);
15731        });
15732    });
15733}
15734
15735pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15736    cx.update(|cx| {
15737        assets::Assets.load_test_fonts(cx);
15738        let store = SettingsStore::test(cx);
15739        cx.set_global(store);
15740        theme::init(theme::LoadThemes::JustBase, cx);
15741        release_channel::init(SemanticVersion::default(), cx);
15742        client::init_settings(cx);
15743        language::init(cx);
15744        Project::init_settings(cx);
15745        workspace::init_settings(cx);
15746        crate::init(cx);
15747    });
15748
15749    update_test_language_settings(cx, f);
15750}
15751
15752#[track_caller]
15753fn assert_hunk_revert(
15754    not_reverted_text_with_selections: &str,
15755    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15756    expected_reverted_text_with_selections: &str,
15757    base_text: &str,
15758    cx: &mut EditorLspTestContext,
15759) {
15760    cx.set_state(not_reverted_text_with_selections);
15761    cx.set_diff_base(base_text);
15762    cx.executor().run_until_parked();
15763
15764    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15765        let snapshot = editor.snapshot(window, cx);
15766        let reverted_hunk_statuses = snapshot
15767            .buffer_snapshot
15768            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15769            .map(|hunk| hunk.status())
15770            .collect::<Vec<_>>();
15771
15772        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15773        reverted_hunk_statuses
15774    });
15775    cx.executor().run_until_parked();
15776    cx.assert_editor_state(expected_reverted_text_with_selections);
15777    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15778}