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
11115    update_test_project_settings(cx, |project_settings| {
11116        project_settings.lsp.insert(
11117            language_server_name.into(),
11118            LspSettings {
11119                binary: None,
11120                settings: None,
11121                initialization_options: Some(json!({
11122                    "anotherInitValue": false
11123                })),
11124            },
11125        );
11126    });
11127    cx.executor().run_until_parked();
11128    assert_eq!(
11129        server_restarts.load(atomic::Ordering::Acquire),
11130        1,
11131        "Should restart LSP server on a related LSP settings change"
11132    );
11133
11134    update_test_project_settings(cx, |project_settings| {
11135        project_settings.lsp.insert(
11136            language_server_name.into(),
11137            LspSettings {
11138                binary: None,
11139                settings: None,
11140                initialization_options: Some(json!({
11141                    "anotherInitValue": false
11142                })),
11143            },
11144        );
11145    });
11146    cx.executor().run_until_parked();
11147    assert_eq!(
11148        server_restarts.load(atomic::Ordering::Acquire),
11149        1,
11150        "Should not restart LSP server on a related LSP settings change that is the same"
11151    );
11152
11153    update_test_project_settings(cx, |project_settings| {
11154        project_settings.lsp.insert(
11155            language_server_name.into(),
11156            LspSettings {
11157                binary: None,
11158                settings: None,
11159                initialization_options: None,
11160            },
11161        );
11162    });
11163    cx.executor().run_until_parked();
11164    assert_eq!(
11165        server_restarts.load(atomic::Ordering::Acquire),
11166        2,
11167        "Should restart LSP server on another related LSP settings change"
11168    );
11169}
11170
11171#[gpui::test]
11172async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11173    init_test(cx, |_| {});
11174
11175    let mut cx = EditorLspTestContext::new_rust(
11176        lsp::ServerCapabilities {
11177            completion_provider: Some(lsp::CompletionOptions {
11178                trigger_characters: Some(vec![".".to_string()]),
11179                resolve_provider: Some(true),
11180                ..Default::default()
11181            }),
11182            ..Default::default()
11183        },
11184        cx,
11185    )
11186    .await;
11187
11188    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11189    cx.simulate_keystroke(".");
11190    let completion_item = lsp::CompletionItem {
11191        label: "some".into(),
11192        kind: Some(lsp::CompletionItemKind::SNIPPET),
11193        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11194        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11195            kind: lsp::MarkupKind::Markdown,
11196            value: "```rust\nSome(2)\n```".to_string(),
11197        })),
11198        deprecated: Some(false),
11199        sort_text: Some("fffffff2".to_string()),
11200        filter_text: Some("some".to_string()),
11201        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11202        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11203            range: lsp::Range {
11204                start: lsp::Position {
11205                    line: 0,
11206                    character: 22,
11207                },
11208                end: lsp::Position {
11209                    line: 0,
11210                    character: 22,
11211                },
11212            },
11213            new_text: "Some(2)".to_string(),
11214        })),
11215        additional_text_edits: Some(vec![lsp::TextEdit {
11216            range: lsp::Range {
11217                start: lsp::Position {
11218                    line: 0,
11219                    character: 20,
11220                },
11221                end: lsp::Position {
11222                    line: 0,
11223                    character: 22,
11224                },
11225            },
11226            new_text: "".to_string(),
11227        }]),
11228        ..Default::default()
11229    };
11230
11231    let closure_completion_item = completion_item.clone();
11232    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11233        let task_completion_item = closure_completion_item.clone();
11234        async move {
11235            Ok(Some(lsp::CompletionResponse::Array(vec![
11236                task_completion_item,
11237            ])))
11238        }
11239    });
11240
11241    request.next().await;
11242
11243    cx.condition(|editor, _| editor.context_menu_visible())
11244        .await;
11245    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11246        editor
11247            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11248            .unwrap()
11249    });
11250    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11251
11252    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11253        let task_completion_item = completion_item.clone();
11254        async move { Ok(task_completion_item) }
11255    })
11256    .next()
11257    .await
11258    .unwrap();
11259    apply_additional_edits.await.unwrap();
11260    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11261}
11262
11263#[gpui::test]
11264async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11265    cx: &mut gpui::TestAppContext,
11266) {
11267    init_test(cx, |_| {});
11268
11269    let mut cx = EditorLspTestContext::new_rust(
11270        lsp::ServerCapabilities {
11271            completion_provider: Some(lsp::CompletionOptions {
11272                trigger_characters: Some(vec![".".to_string()]),
11273                resolve_provider: Some(true),
11274                ..Default::default()
11275            }),
11276            ..Default::default()
11277        },
11278        cx,
11279    )
11280    .await;
11281
11282    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11283    cx.simulate_keystroke(".");
11284
11285    let item1 = lsp::CompletionItem {
11286        label: "id".to_string(),
11287        filter_text: Some("id".to_string()),
11288        detail: None,
11289        documentation: None,
11290        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11291            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11292            new_text: ".id".to_string(),
11293        })),
11294        ..lsp::CompletionItem::default()
11295    };
11296
11297    let item2 = lsp::CompletionItem {
11298        label: "other".to_string(),
11299        filter_text: Some("other".to_string()),
11300        detail: None,
11301        documentation: None,
11302        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11303            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11304            new_text: ".other".to_string(),
11305        })),
11306        ..lsp::CompletionItem::default()
11307    };
11308
11309    let item1 = item1.clone();
11310    cx.handle_request::<lsp::request::Completion, _, _>({
11311        let item1 = item1.clone();
11312        move |_, _, _| {
11313            let item1 = item1.clone();
11314            let item2 = item2.clone();
11315            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11316        }
11317    })
11318    .next()
11319    .await;
11320
11321    cx.condition(|editor, _| editor.context_menu_visible())
11322        .await;
11323    cx.update_editor(|editor, _, _| {
11324        let context_menu = editor.context_menu.borrow_mut();
11325        let context_menu = context_menu
11326            .as_ref()
11327            .expect("Should have the context menu deployed");
11328        match context_menu {
11329            CodeContextMenu::Completions(completions_menu) => {
11330                let completions = completions_menu.completions.borrow_mut();
11331                assert_eq!(
11332                    completions
11333                        .iter()
11334                        .map(|completion| &completion.label.text)
11335                        .collect::<Vec<_>>(),
11336                    vec!["id", "other"]
11337                )
11338            }
11339            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11340        }
11341    });
11342
11343    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11344        let item1 = item1.clone();
11345        move |_, item_to_resolve, _| {
11346            let item1 = item1.clone();
11347            async move {
11348                if item1 == item_to_resolve {
11349                    Ok(lsp::CompletionItem {
11350                        label: "method id()".to_string(),
11351                        filter_text: Some("id".to_string()),
11352                        detail: Some("Now resolved!".to_string()),
11353                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11354                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11355                            range: lsp::Range::new(
11356                                lsp::Position::new(0, 22),
11357                                lsp::Position::new(0, 22),
11358                            ),
11359                            new_text: ".id".to_string(),
11360                        })),
11361                        ..lsp::CompletionItem::default()
11362                    })
11363                } else {
11364                    Ok(item_to_resolve)
11365                }
11366            }
11367        }
11368    })
11369    .next()
11370    .await
11371    .unwrap();
11372    cx.run_until_parked();
11373
11374    cx.update_editor(|editor, window, cx| {
11375        editor.context_menu_next(&Default::default(), window, cx);
11376    });
11377
11378    cx.update_editor(|editor, _, _| {
11379        let context_menu = editor.context_menu.borrow_mut();
11380        let context_menu = context_menu
11381            .as_ref()
11382            .expect("Should have the context menu deployed");
11383        match context_menu {
11384            CodeContextMenu::Completions(completions_menu) => {
11385                let completions = completions_menu.completions.borrow_mut();
11386                assert_eq!(
11387                    completions
11388                        .iter()
11389                        .map(|completion| &completion.label.text)
11390                        .collect::<Vec<_>>(),
11391                    vec!["method id()", "other"],
11392                    "Should update first completion label, but not second as the filter text did not match."
11393                );
11394            }
11395            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11396        }
11397    });
11398}
11399
11400#[gpui::test]
11401async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11402    init_test(cx, |_| {});
11403
11404    let mut cx = EditorLspTestContext::new_rust(
11405        lsp::ServerCapabilities {
11406            completion_provider: Some(lsp::CompletionOptions {
11407                trigger_characters: Some(vec![".".to_string()]),
11408                resolve_provider: Some(true),
11409                ..Default::default()
11410            }),
11411            ..Default::default()
11412        },
11413        cx,
11414    )
11415    .await;
11416
11417    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11418    cx.simulate_keystroke(".");
11419
11420    let unresolved_item_1 = lsp::CompletionItem {
11421        label: "id".to_string(),
11422        filter_text: Some("id".to_string()),
11423        detail: None,
11424        documentation: None,
11425        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11426            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11427            new_text: ".id".to_string(),
11428        })),
11429        ..lsp::CompletionItem::default()
11430    };
11431    let resolved_item_1 = lsp::CompletionItem {
11432        additional_text_edits: Some(vec![lsp::TextEdit {
11433            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11434            new_text: "!!".to_string(),
11435        }]),
11436        ..unresolved_item_1.clone()
11437    };
11438    let unresolved_item_2 = lsp::CompletionItem {
11439        label: "other".to_string(),
11440        filter_text: Some("other".to_string()),
11441        detail: None,
11442        documentation: None,
11443        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11444            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11445            new_text: ".other".to_string(),
11446        })),
11447        ..lsp::CompletionItem::default()
11448    };
11449    let resolved_item_2 = lsp::CompletionItem {
11450        additional_text_edits: Some(vec![lsp::TextEdit {
11451            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11452            new_text: "??".to_string(),
11453        }]),
11454        ..unresolved_item_2.clone()
11455    };
11456
11457    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11458    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11459    cx.lsp
11460        .server
11461        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11462            let unresolved_item_1 = unresolved_item_1.clone();
11463            let resolved_item_1 = resolved_item_1.clone();
11464            let unresolved_item_2 = unresolved_item_2.clone();
11465            let resolved_item_2 = resolved_item_2.clone();
11466            let resolve_requests_1 = resolve_requests_1.clone();
11467            let resolve_requests_2 = resolve_requests_2.clone();
11468            move |unresolved_request, _| {
11469                let unresolved_item_1 = unresolved_item_1.clone();
11470                let resolved_item_1 = resolved_item_1.clone();
11471                let unresolved_item_2 = unresolved_item_2.clone();
11472                let resolved_item_2 = resolved_item_2.clone();
11473                let resolve_requests_1 = resolve_requests_1.clone();
11474                let resolve_requests_2 = resolve_requests_2.clone();
11475                async move {
11476                    if unresolved_request == unresolved_item_1 {
11477                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11478                        Ok(resolved_item_1.clone())
11479                    } else if unresolved_request == unresolved_item_2 {
11480                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11481                        Ok(resolved_item_2.clone())
11482                    } else {
11483                        panic!("Unexpected completion item {unresolved_request:?}")
11484                    }
11485                }
11486            }
11487        })
11488        .detach();
11489
11490    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11491        let unresolved_item_1 = unresolved_item_1.clone();
11492        let unresolved_item_2 = unresolved_item_2.clone();
11493        async move {
11494            Ok(Some(lsp::CompletionResponse::Array(vec![
11495                unresolved_item_1,
11496                unresolved_item_2,
11497            ])))
11498        }
11499    })
11500    .next()
11501    .await;
11502
11503    cx.condition(|editor, _| editor.context_menu_visible())
11504        .await;
11505    cx.update_editor(|editor, _, _| {
11506        let context_menu = editor.context_menu.borrow_mut();
11507        let context_menu = context_menu
11508            .as_ref()
11509            .expect("Should have the context menu deployed");
11510        match context_menu {
11511            CodeContextMenu::Completions(completions_menu) => {
11512                let completions = completions_menu.completions.borrow_mut();
11513                assert_eq!(
11514                    completions
11515                        .iter()
11516                        .map(|completion| &completion.label.text)
11517                        .collect::<Vec<_>>(),
11518                    vec!["id", "other"]
11519                )
11520            }
11521            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11522        }
11523    });
11524    cx.run_until_parked();
11525
11526    cx.update_editor(|editor, window, cx| {
11527        editor.context_menu_next(&ContextMenuNext, window, cx);
11528    });
11529    cx.run_until_parked();
11530    cx.update_editor(|editor, window, cx| {
11531        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11532    });
11533    cx.run_until_parked();
11534    cx.update_editor(|editor, window, cx| {
11535        editor.context_menu_next(&ContextMenuNext, window, cx);
11536    });
11537    cx.run_until_parked();
11538    cx.update_editor(|editor, window, cx| {
11539        editor
11540            .compose_completion(&ComposeCompletion::default(), window, cx)
11541            .expect("No task returned")
11542    })
11543    .await
11544    .expect("Completion failed");
11545    cx.run_until_parked();
11546
11547    cx.update_editor(|editor, _, cx| {
11548        assert_eq!(
11549            resolve_requests_1.load(atomic::Ordering::Acquire),
11550            1,
11551            "Should always resolve once despite multiple selections"
11552        );
11553        assert_eq!(
11554            resolve_requests_2.load(atomic::Ordering::Acquire),
11555            1,
11556            "Should always resolve once after multiple selections and applying the completion"
11557        );
11558        assert_eq!(
11559            editor.text(cx),
11560            "fn main() { let a = ??.other; }",
11561            "Should use resolved data when applying the completion"
11562        );
11563    });
11564}
11565
11566#[gpui::test]
11567async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11568    init_test(cx, |_| {});
11569
11570    let item_0 = lsp::CompletionItem {
11571        label: "abs".into(),
11572        insert_text: Some("abs".into()),
11573        data: Some(json!({ "very": "special"})),
11574        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11575        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11576            lsp::InsertReplaceEdit {
11577                new_text: "abs".to_string(),
11578                insert: lsp::Range::default(),
11579                replace: lsp::Range::default(),
11580            },
11581        )),
11582        ..lsp::CompletionItem::default()
11583    };
11584    let items = iter::once(item_0.clone())
11585        .chain((11..51).map(|i| lsp::CompletionItem {
11586            label: format!("item_{}", i),
11587            insert_text: Some(format!("item_{}", i)),
11588            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11589            ..lsp::CompletionItem::default()
11590        }))
11591        .collect::<Vec<_>>();
11592
11593    let default_commit_characters = vec!["?".to_string()];
11594    let default_data = json!({ "default": "data"});
11595    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11596    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11597    let default_edit_range = lsp::Range {
11598        start: lsp::Position {
11599            line: 0,
11600            character: 5,
11601        },
11602        end: lsp::Position {
11603            line: 0,
11604            character: 5,
11605        },
11606    };
11607
11608    let item_0_out = lsp::CompletionItem {
11609        commit_characters: Some(default_commit_characters.clone()),
11610        insert_text_format: Some(default_insert_text_format),
11611        ..item_0
11612    };
11613    let items_out = iter::once(item_0_out)
11614        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11615            commit_characters: Some(default_commit_characters.clone()),
11616            data: Some(default_data.clone()),
11617            insert_text_mode: Some(default_insert_text_mode),
11618            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11619                range: default_edit_range,
11620                new_text: item.label.clone(),
11621            })),
11622            ..item.clone()
11623        }))
11624        .collect::<Vec<lsp::CompletionItem>>();
11625
11626    let mut cx = EditorLspTestContext::new_rust(
11627        lsp::ServerCapabilities {
11628            completion_provider: Some(lsp::CompletionOptions {
11629                trigger_characters: Some(vec![".".to_string()]),
11630                resolve_provider: Some(true),
11631                ..Default::default()
11632            }),
11633            ..Default::default()
11634        },
11635        cx,
11636    )
11637    .await;
11638
11639    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11640    cx.simulate_keystroke(".");
11641
11642    let completion_data = default_data.clone();
11643    let completion_characters = default_commit_characters.clone();
11644    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11645        let default_data = completion_data.clone();
11646        let default_commit_characters = completion_characters.clone();
11647        let items = items.clone();
11648        async move {
11649            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11650                items,
11651                item_defaults: Some(lsp::CompletionListItemDefaults {
11652                    data: Some(default_data.clone()),
11653                    commit_characters: Some(default_commit_characters.clone()),
11654                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11655                        default_edit_range,
11656                    )),
11657                    insert_text_format: Some(default_insert_text_format),
11658                    insert_text_mode: Some(default_insert_text_mode),
11659                }),
11660                ..lsp::CompletionList::default()
11661            })))
11662        }
11663    })
11664    .next()
11665    .await;
11666
11667    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11668    cx.lsp
11669        .server
11670        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11671            let closure_resolved_items = resolved_items.clone();
11672            move |item_to_resolve, _| {
11673                let closure_resolved_items = closure_resolved_items.clone();
11674                async move {
11675                    closure_resolved_items.lock().push(item_to_resolve.clone());
11676                    Ok(item_to_resolve)
11677                }
11678            }
11679        })
11680        .detach();
11681
11682    cx.condition(|editor, _| editor.context_menu_visible())
11683        .await;
11684    cx.run_until_parked();
11685    cx.update_editor(|editor, _, _| {
11686        let menu = editor.context_menu.borrow_mut();
11687        match menu.as_ref().expect("should have the completions menu") {
11688            CodeContextMenu::Completions(completions_menu) => {
11689                assert_eq!(
11690                    completions_menu
11691                        .entries
11692                        .borrow()
11693                        .iter()
11694                        .flat_map(|c| match c {
11695                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11696                            _ => None,
11697                        })
11698                        .collect::<Vec<String>>(),
11699                    items_out
11700                        .iter()
11701                        .map(|completion| completion.label.clone())
11702                        .collect::<Vec<String>>()
11703                );
11704            }
11705            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11706        }
11707    });
11708    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11709    // with 4 from the end.
11710    assert_eq!(
11711        *resolved_items.lock(),
11712        [
11713            &items_out[0..16],
11714            &items_out[items_out.len() - 4..items_out.len()]
11715        ]
11716        .concat()
11717        .iter()
11718        .cloned()
11719        .collect::<Vec<lsp::CompletionItem>>()
11720    );
11721    resolved_items.lock().clear();
11722
11723    cx.update_editor(|editor, window, cx| {
11724        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11725    });
11726    cx.run_until_parked();
11727    // Completions that have already been resolved are skipped.
11728    assert_eq!(
11729        *resolved_items.lock(),
11730        items_out[items_out.len() - 16..items_out.len() - 4]
11731            .iter()
11732            .cloned()
11733            .collect::<Vec<lsp::CompletionItem>>()
11734    );
11735    resolved_items.lock().clear();
11736}
11737
11738#[gpui::test]
11739async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11740    init_test(cx, |_| {});
11741
11742    let mut cx = EditorLspTestContext::new(
11743        Language::new(
11744            LanguageConfig {
11745                matcher: LanguageMatcher {
11746                    path_suffixes: vec!["jsx".into()],
11747                    ..Default::default()
11748                },
11749                overrides: [(
11750                    "element".into(),
11751                    LanguageConfigOverride {
11752                        word_characters: Override::Set(['-'].into_iter().collect()),
11753                        ..Default::default()
11754                    },
11755                )]
11756                .into_iter()
11757                .collect(),
11758                ..Default::default()
11759            },
11760            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11761        )
11762        .with_override_query("(jsx_self_closing_element) @element")
11763        .unwrap(),
11764        lsp::ServerCapabilities {
11765            completion_provider: Some(lsp::CompletionOptions {
11766                trigger_characters: Some(vec![":".to_string()]),
11767                ..Default::default()
11768            }),
11769            ..Default::default()
11770        },
11771        cx,
11772    )
11773    .await;
11774
11775    cx.lsp
11776        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11777            Ok(Some(lsp::CompletionResponse::Array(vec![
11778                lsp::CompletionItem {
11779                    label: "bg-blue".into(),
11780                    ..Default::default()
11781                },
11782                lsp::CompletionItem {
11783                    label: "bg-red".into(),
11784                    ..Default::default()
11785                },
11786                lsp::CompletionItem {
11787                    label: "bg-yellow".into(),
11788                    ..Default::default()
11789                },
11790            ])))
11791        });
11792
11793    cx.set_state(r#"<p class="bgˇ" />"#);
11794
11795    // Trigger completion when typing a dash, because the dash is an extra
11796    // word character in the 'element' scope, which contains the cursor.
11797    cx.simulate_keystroke("-");
11798    cx.executor().run_until_parked();
11799    cx.update_editor(|editor, _, _| {
11800        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11801        {
11802            assert_eq!(
11803                completion_menu_entries(&menu),
11804                &["bg-red", "bg-blue", "bg-yellow"]
11805            );
11806        } else {
11807            panic!("expected completion menu to be open");
11808        }
11809    });
11810
11811    cx.simulate_keystroke("l");
11812    cx.executor().run_until_parked();
11813    cx.update_editor(|editor, _, _| {
11814        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11815        {
11816            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11817        } else {
11818            panic!("expected completion menu to be open");
11819        }
11820    });
11821
11822    // When filtering completions, consider the character after the '-' to
11823    // be the start of a subword.
11824    cx.set_state(r#"<p class="yelˇ" />"#);
11825    cx.simulate_keystroke("l");
11826    cx.executor().run_until_parked();
11827    cx.update_editor(|editor, _, _| {
11828        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11829        {
11830            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11831        } else {
11832            panic!("expected completion menu to be open");
11833        }
11834    });
11835}
11836
11837fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11838    let entries = menu.entries.borrow();
11839    entries
11840        .iter()
11841        .flat_map(|e| match e {
11842            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11843            _ => None,
11844        })
11845        .collect()
11846}
11847
11848#[gpui::test]
11849async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11850    init_test(cx, |settings| {
11851        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11852            FormatterList(vec![Formatter::Prettier].into()),
11853        ))
11854    });
11855
11856    let fs = FakeFs::new(cx.executor());
11857    fs.insert_file("/file.ts", Default::default()).await;
11858
11859    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11860    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11861
11862    language_registry.add(Arc::new(Language::new(
11863        LanguageConfig {
11864            name: "TypeScript".into(),
11865            matcher: LanguageMatcher {
11866                path_suffixes: vec!["ts".to_string()],
11867                ..Default::default()
11868            },
11869            ..Default::default()
11870        },
11871        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11872    )));
11873    update_test_language_settings(cx, |settings| {
11874        settings.defaults.prettier = Some(PrettierSettings {
11875            allowed: true,
11876            ..PrettierSettings::default()
11877        });
11878    });
11879
11880    let test_plugin = "test_plugin";
11881    let _ = language_registry.register_fake_lsp(
11882        "TypeScript",
11883        FakeLspAdapter {
11884            prettier_plugins: vec![test_plugin],
11885            ..Default::default()
11886        },
11887    );
11888
11889    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11890    let buffer = project
11891        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11892        .await
11893        .unwrap();
11894
11895    let buffer_text = "one\ntwo\nthree\n";
11896    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11897    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11898    editor.update_in(cx, |editor, window, cx| {
11899        editor.set_text(buffer_text, window, cx)
11900    });
11901
11902    editor
11903        .update_in(cx, |editor, window, cx| {
11904            editor.perform_format(
11905                project.clone(),
11906                FormatTrigger::Manual,
11907                FormatTarget::Buffers,
11908                window,
11909                cx,
11910            )
11911        })
11912        .unwrap()
11913        .await;
11914    assert_eq!(
11915        editor.update(cx, |editor, cx| editor.text(cx)),
11916        buffer_text.to_string() + prettier_format_suffix,
11917        "Test prettier formatting was not applied to the original buffer text",
11918    );
11919
11920    update_test_language_settings(cx, |settings| {
11921        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11922    });
11923    let format = editor.update_in(cx, |editor, window, cx| {
11924        editor.perform_format(
11925            project.clone(),
11926            FormatTrigger::Manual,
11927            FormatTarget::Buffers,
11928            window,
11929            cx,
11930        )
11931    });
11932    format.await.unwrap();
11933    assert_eq!(
11934        editor.update(cx, |editor, cx| editor.text(cx)),
11935        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11936        "Autoformatting (via test prettier) was not applied to the original buffer text",
11937    );
11938}
11939
11940#[gpui::test]
11941async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11942    init_test(cx, |_| {});
11943    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11944    let base_text = indoc! {r#"
11945        struct Row;
11946        struct Row1;
11947        struct Row2;
11948
11949        struct Row4;
11950        struct Row5;
11951        struct Row6;
11952
11953        struct Row8;
11954        struct Row9;
11955        struct Row10;"#};
11956
11957    // When addition hunks are not adjacent to carets, no hunk revert is performed
11958    assert_hunk_revert(
11959        indoc! {r#"struct Row;
11960                   struct Row1;
11961                   struct Row1.1;
11962                   struct Row1.2;
11963                   struct Row2;ˇ
11964
11965                   struct Row4;
11966                   struct Row5;
11967                   struct Row6;
11968
11969                   struct Row8;
11970                   ˇstruct Row9;
11971                   struct Row9.1;
11972                   struct Row9.2;
11973                   struct Row9.3;
11974                   struct Row10;"#},
11975        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11976        indoc! {r#"struct Row;
11977                   struct Row1;
11978                   struct Row1.1;
11979                   struct Row1.2;
11980                   struct Row2;ˇ
11981
11982                   struct Row4;
11983                   struct Row5;
11984                   struct Row6;
11985
11986                   struct Row8;
11987                   ˇstruct Row9;
11988                   struct Row9.1;
11989                   struct Row9.2;
11990                   struct Row9.3;
11991                   struct Row10;"#},
11992        base_text,
11993        &mut cx,
11994    );
11995    // Same for selections
11996    assert_hunk_revert(
11997        indoc! {r#"struct Row;
11998                   struct Row1;
11999                   struct Row2;
12000                   struct Row2.1;
12001                   struct Row2.2;
12002                   «ˇ
12003                   struct Row4;
12004                   struct» Row5;
12005                   «struct Row6;
12006                   ˇ»
12007                   struct Row9.1;
12008                   struct Row9.2;
12009                   struct Row9.3;
12010                   struct Row8;
12011                   struct Row9;
12012                   struct Row10;"#},
12013        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12014        indoc! {r#"struct Row;
12015                   struct Row1;
12016                   struct Row2;
12017                   struct Row2.1;
12018                   struct Row2.2;
12019                   «ˇ
12020                   struct Row4;
12021                   struct» Row5;
12022                   «struct Row6;
12023                   ˇ»
12024                   struct Row9.1;
12025                   struct Row9.2;
12026                   struct Row9.3;
12027                   struct Row8;
12028                   struct Row9;
12029                   struct Row10;"#},
12030        base_text,
12031        &mut cx,
12032    );
12033
12034    // When carets and selections intersect the addition hunks, those are reverted.
12035    // Adjacent carets got merged.
12036    assert_hunk_revert(
12037        indoc! {r#"struct Row;
12038                   ˇ// something on the top
12039                   struct Row1;
12040                   struct Row2;
12041                   struct Roˇw3.1;
12042                   struct Row2.2;
12043                   struct Row2.3;ˇ
12044
12045                   struct Row4;
12046                   struct ˇRow5.1;
12047                   struct Row5.2;
12048                   struct «Rowˇ»5.3;
12049                   struct Row5;
12050                   struct Row6;
12051                   ˇ
12052                   struct Row9.1;
12053                   struct «Rowˇ»9.2;
12054                   struct «ˇRow»9.3;
12055                   struct Row8;
12056                   struct Row9;
12057                   «ˇ// something on bottom»
12058                   struct Row10;"#},
12059        vec![
12060            DiffHunkStatus::Added,
12061            DiffHunkStatus::Added,
12062            DiffHunkStatus::Added,
12063            DiffHunkStatus::Added,
12064            DiffHunkStatus::Added,
12065        ],
12066        indoc! {r#"struct Row;
12067                   ˇstruct Row1;
12068                   struct Row2;
12069                   ˇ
12070                   struct Row4;
12071                   ˇstruct Row5;
12072                   struct Row6;
12073                   ˇ
12074                   ˇstruct Row8;
12075                   struct Row9;
12076                   ˇstruct Row10;"#},
12077        base_text,
12078        &mut cx,
12079    );
12080}
12081
12082#[gpui::test]
12083async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12084    init_test(cx, |_| {});
12085    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12086    let base_text = indoc! {r#"
12087        struct Row;
12088        struct Row1;
12089        struct Row2;
12090
12091        struct Row4;
12092        struct Row5;
12093        struct Row6;
12094
12095        struct Row8;
12096        struct Row9;
12097        struct Row10;"#};
12098
12099    // Modification hunks behave the same as the addition ones.
12100    assert_hunk_revert(
12101        indoc! {r#"struct Row;
12102                   struct Row1;
12103                   struct Row33;
12104                   ˇ
12105                   struct Row4;
12106                   struct Row5;
12107                   struct Row6;
12108                   ˇ
12109                   struct Row99;
12110                   struct Row9;
12111                   struct Row10;"#},
12112        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12113        indoc! {r#"struct Row;
12114                   struct Row1;
12115                   struct Row33;
12116                   ˇ
12117                   struct Row4;
12118                   struct Row5;
12119                   struct Row6;
12120                   ˇ
12121                   struct Row99;
12122                   struct Row9;
12123                   struct Row10;"#},
12124        base_text,
12125        &mut cx,
12126    );
12127    assert_hunk_revert(
12128        indoc! {r#"struct Row;
12129                   struct Row1;
12130                   struct Row33;
12131                   «ˇ
12132                   struct Row4;
12133                   struct» Row5;
12134                   «struct Row6;
12135                   ˇ»
12136                   struct Row99;
12137                   struct Row9;
12138                   struct Row10;"#},
12139        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12140        indoc! {r#"struct Row;
12141                   struct Row1;
12142                   struct Row33;
12143                   «ˇ
12144                   struct Row4;
12145                   struct» Row5;
12146                   «struct Row6;
12147                   ˇ»
12148                   struct Row99;
12149                   struct Row9;
12150                   struct Row10;"#},
12151        base_text,
12152        &mut cx,
12153    );
12154
12155    assert_hunk_revert(
12156        indoc! {r#"ˇstruct Row1.1;
12157                   struct Row1;
12158                   «ˇstr»uct Row22;
12159
12160                   struct ˇRow44;
12161                   struct Row5;
12162                   struct «Rˇ»ow66;ˇ
12163
12164                   «struˇ»ct Row88;
12165                   struct Row9;
12166                   struct Row1011;ˇ"#},
12167        vec![
12168            DiffHunkStatus::Modified,
12169            DiffHunkStatus::Modified,
12170            DiffHunkStatus::Modified,
12171            DiffHunkStatus::Modified,
12172            DiffHunkStatus::Modified,
12173            DiffHunkStatus::Modified,
12174        ],
12175        indoc! {r#"struct Row;
12176                   ˇstruct Row1;
12177                   struct Row2;
12178                   ˇ
12179                   struct Row4;
12180                   ˇstruct Row5;
12181                   struct Row6;
12182                   ˇ
12183                   struct Row8;
12184                   ˇstruct Row9;
12185                   struct Row10;ˇ"#},
12186        base_text,
12187        &mut cx,
12188    );
12189}
12190
12191#[gpui::test]
12192async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12193    init_test(cx, |_| {});
12194    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12195    let base_text = indoc! {r#"
12196        one
12197
12198        two
12199        three
12200        "#};
12201
12202    cx.set_diff_base(base_text);
12203    cx.set_state("\nˇ\n");
12204    cx.executor().run_until_parked();
12205    cx.update_editor(|editor, _window, cx| {
12206        editor.expand_selected_diff_hunks(cx);
12207    });
12208    cx.executor().run_until_parked();
12209    cx.update_editor(|editor, window, cx| {
12210        editor.backspace(&Default::default(), window, cx);
12211    });
12212    cx.run_until_parked();
12213    cx.assert_state_with_diff(
12214        indoc! {r#"
12215
12216        - two
12217        - threeˇ
12218        +
12219        "#}
12220        .to_string(),
12221    );
12222}
12223
12224#[gpui::test]
12225async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12226    init_test(cx, |_| {});
12227    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12228    let base_text = indoc! {r#"struct Row;
12229struct Row1;
12230struct Row2;
12231
12232struct Row4;
12233struct Row5;
12234struct Row6;
12235
12236struct Row8;
12237struct Row9;
12238struct Row10;"#};
12239
12240    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12241    assert_hunk_revert(
12242        indoc! {r#"struct Row;
12243                   struct Row2;
12244
12245                   ˇstruct Row4;
12246                   struct Row5;
12247                   struct Row6;
12248                   ˇ
12249                   struct Row8;
12250                   struct Row10;"#},
12251        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12252        indoc! {r#"struct Row;
12253                   struct Row2;
12254
12255                   ˇstruct Row4;
12256                   struct Row5;
12257                   struct Row6;
12258                   ˇ
12259                   struct Row8;
12260                   struct Row10;"#},
12261        base_text,
12262        &mut cx,
12263    );
12264    assert_hunk_revert(
12265        indoc! {r#"struct Row;
12266                   struct Row2;
12267
12268                   «ˇstruct Row4;
12269                   struct» Row5;
12270                   «struct Row6;
12271                   ˇ»
12272                   struct Row8;
12273                   struct Row10;"#},
12274        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12275        indoc! {r#"struct Row;
12276                   struct Row2;
12277
12278                   «ˇstruct Row4;
12279                   struct» Row5;
12280                   «struct Row6;
12281                   ˇ»
12282                   struct Row8;
12283                   struct Row10;"#},
12284        base_text,
12285        &mut cx,
12286    );
12287
12288    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12289    assert_hunk_revert(
12290        indoc! {r#"struct Row;
12291                   ˇstruct Row2;
12292
12293                   struct Row4;
12294                   struct Row5;
12295                   struct Row6;
12296
12297                   struct Row8;ˇ
12298                   struct Row10;"#},
12299        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12300        indoc! {r#"struct Row;
12301                   struct Row1;
12302                   ˇstruct Row2;
12303
12304                   struct Row4;
12305                   struct Row5;
12306                   struct Row6;
12307
12308                   struct Row8;ˇ
12309                   struct Row9;
12310                   struct Row10;"#},
12311        base_text,
12312        &mut cx,
12313    );
12314    assert_hunk_revert(
12315        indoc! {r#"struct Row;
12316                   struct Row2«ˇ;
12317                   struct Row4;
12318                   struct» Row5;
12319                   «struct Row6;
12320
12321                   struct Row8;ˇ»
12322                   struct Row10;"#},
12323        vec![
12324            DiffHunkStatus::Removed,
12325            DiffHunkStatus::Removed,
12326            DiffHunkStatus::Removed,
12327        ],
12328        indoc! {r#"struct Row;
12329                   struct Row1;
12330                   struct Row2«ˇ;
12331
12332                   struct Row4;
12333                   struct» Row5;
12334                   «struct Row6;
12335
12336                   struct Row8;ˇ»
12337                   struct Row9;
12338                   struct Row10;"#},
12339        base_text,
12340        &mut cx,
12341    );
12342}
12343
12344#[gpui::test]
12345async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12346    init_test(cx, |_| {});
12347
12348    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12349    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12350    let base_text_3 =
12351        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12352
12353    let text_1 = edit_first_char_of_every_line(base_text_1);
12354    let text_2 = edit_first_char_of_every_line(base_text_2);
12355    let text_3 = edit_first_char_of_every_line(base_text_3);
12356
12357    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12358    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12359    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12360
12361    let multibuffer = cx.new(|cx| {
12362        let mut multibuffer = MultiBuffer::new(ReadWrite);
12363        multibuffer.push_excerpts(
12364            buffer_1.clone(),
12365            [
12366                ExcerptRange {
12367                    context: Point::new(0, 0)..Point::new(3, 0),
12368                    primary: None,
12369                },
12370                ExcerptRange {
12371                    context: Point::new(5, 0)..Point::new(7, 0),
12372                    primary: None,
12373                },
12374                ExcerptRange {
12375                    context: Point::new(9, 0)..Point::new(10, 4),
12376                    primary: None,
12377                },
12378            ],
12379            cx,
12380        );
12381        multibuffer.push_excerpts(
12382            buffer_2.clone(),
12383            [
12384                ExcerptRange {
12385                    context: Point::new(0, 0)..Point::new(3, 0),
12386                    primary: None,
12387                },
12388                ExcerptRange {
12389                    context: Point::new(5, 0)..Point::new(7, 0),
12390                    primary: None,
12391                },
12392                ExcerptRange {
12393                    context: Point::new(9, 0)..Point::new(10, 4),
12394                    primary: None,
12395                },
12396            ],
12397            cx,
12398        );
12399        multibuffer.push_excerpts(
12400            buffer_3.clone(),
12401            [
12402                ExcerptRange {
12403                    context: Point::new(0, 0)..Point::new(3, 0),
12404                    primary: None,
12405                },
12406                ExcerptRange {
12407                    context: Point::new(5, 0)..Point::new(7, 0),
12408                    primary: None,
12409                },
12410                ExcerptRange {
12411                    context: Point::new(9, 0)..Point::new(10, 4),
12412                    primary: None,
12413                },
12414            ],
12415            cx,
12416        );
12417        multibuffer
12418    });
12419
12420    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12421    editor.update_in(cx, |editor, _window, cx| {
12422        for (buffer, diff_base) in [
12423            (buffer_1.clone(), base_text_1),
12424            (buffer_2.clone(), base_text_2),
12425            (buffer_3.clone(), base_text_3),
12426        ] {
12427            let change_set = cx
12428                .new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx));
12429            editor
12430                .buffer
12431                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12432        }
12433    });
12434    cx.executor().run_until_parked();
12435
12436    editor.update_in(cx, |editor, window, cx| {
12437        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}");
12438        editor.select_all(&SelectAll, window, cx);
12439        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12440    });
12441    cx.executor().run_until_parked();
12442
12443    // When all ranges are selected, all buffer hunks are reverted.
12444    editor.update(cx, |editor, cx| {
12445        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");
12446    });
12447    buffer_1.update(cx, |buffer, _| {
12448        assert_eq!(buffer.text(), base_text_1);
12449    });
12450    buffer_2.update(cx, |buffer, _| {
12451        assert_eq!(buffer.text(), base_text_2);
12452    });
12453    buffer_3.update(cx, |buffer, _| {
12454        assert_eq!(buffer.text(), base_text_3);
12455    });
12456
12457    editor.update_in(cx, |editor, window, cx| {
12458        editor.undo(&Default::default(), window, cx);
12459    });
12460
12461    editor.update_in(cx, |editor, window, cx| {
12462        editor.change_selections(None, window, cx, |s| {
12463            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12464        });
12465        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12466    });
12467
12468    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12469    // but not affect buffer_2 and its related excerpts.
12470    editor.update(cx, |editor, cx| {
12471        assert_eq!(
12472            editor.text(cx),
12473            "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}"
12474        );
12475    });
12476    buffer_1.update(cx, |buffer, _| {
12477        assert_eq!(buffer.text(), base_text_1);
12478    });
12479    buffer_2.update(cx, |buffer, _| {
12480        assert_eq!(
12481            buffer.text(),
12482            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12483        );
12484    });
12485    buffer_3.update(cx, |buffer, _| {
12486        assert_eq!(
12487            buffer.text(),
12488            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12489        );
12490    });
12491
12492    fn edit_first_char_of_every_line(text: &str) -> String {
12493        text.split('\n')
12494            .map(|line| format!("X{}", &line[1..]))
12495            .collect::<Vec<_>>()
12496            .join("\n")
12497    }
12498}
12499
12500#[gpui::test]
12501async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12502    init_test(cx, |_| {});
12503
12504    let cols = 4;
12505    let rows = 10;
12506    let sample_text_1 = sample_text(rows, cols, 'a');
12507    assert_eq!(
12508        sample_text_1,
12509        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12510    );
12511    let sample_text_2 = sample_text(rows, cols, 'l');
12512    assert_eq!(
12513        sample_text_2,
12514        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12515    );
12516    let sample_text_3 = sample_text(rows, cols, 'v');
12517    assert_eq!(
12518        sample_text_3,
12519        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12520    );
12521
12522    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12523    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12524    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12525
12526    let multi_buffer = cx.new(|cx| {
12527        let mut multibuffer = MultiBuffer::new(ReadWrite);
12528        multibuffer.push_excerpts(
12529            buffer_1.clone(),
12530            [
12531                ExcerptRange {
12532                    context: Point::new(0, 0)..Point::new(3, 0),
12533                    primary: None,
12534                },
12535                ExcerptRange {
12536                    context: Point::new(5, 0)..Point::new(7, 0),
12537                    primary: None,
12538                },
12539                ExcerptRange {
12540                    context: Point::new(9, 0)..Point::new(10, 4),
12541                    primary: None,
12542                },
12543            ],
12544            cx,
12545        );
12546        multibuffer.push_excerpts(
12547            buffer_2.clone(),
12548            [
12549                ExcerptRange {
12550                    context: Point::new(0, 0)..Point::new(3, 0),
12551                    primary: None,
12552                },
12553                ExcerptRange {
12554                    context: Point::new(5, 0)..Point::new(7, 0),
12555                    primary: None,
12556                },
12557                ExcerptRange {
12558                    context: Point::new(9, 0)..Point::new(10, 4),
12559                    primary: None,
12560                },
12561            ],
12562            cx,
12563        );
12564        multibuffer.push_excerpts(
12565            buffer_3.clone(),
12566            [
12567                ExcerptRange {
12568                    context: Point::new(0, 0)..Point::new(3, 0),
12569                    primary: None,
12570                },
12571                ExcerptRange {
12572                    context: Point::new(5, 0)..Point::new(7, 0),
12573                    primary: None,
12574                },
12575                ExcerptRange {
12576                    context: Point::new(9, 0)..Point::new(10, 4),
12577                    primary: None,
12578                },
12579            ],
12580            cx,
12581        );
12582        multibuffer
12583    });
12584
12585    let fs = FakeFs::new(cx.executor());
12586    fs.insert_tree(
12587        "/a",
12588        json!({
12589            "main.rs": sample_text_1,
12590            "other.rs": sample_text_2,
12591            "lib.rs": sample_text_3,
12592        }),
12593    )
12594    .await;
12595    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12596    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12597    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12598    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12599        Editor::new(
12600            EditorMode::Full,
12601            multi_buffer,
12602            Some(project.clone()),
12603            true,
12604            window,
12605            cx,
12606        )
12607    });
12608    let multibuffer_item_id = workspace
12609        .update(cx, |workspace, window, cx| {
12610            assert!(
12611                workspace.active_item(cx).is_none(),
12612                "active item should be None before the first item is added"
12613            );
12614            workspace.add_item_to_active_pane(
12615                Box::new(multi_buffer_editor.clone()),
12616                None,
12617                true,
12618                window,
12619                cx,
12620            );
12621            let active_item = workspace
12622                .active_item(cx)
12623                .expect("should have an active item after adding the multi buffer");
12624            assert!(
12625                !active_item.is_singleton(cx),
12626                "A multi buffer was expected to active after adding"
12627            );
12628            active_item.item_id()
12629        })
12630        .unwrap();
12631    cx.executor().run_until_parked();
12632
12633    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12634        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12635            s.select_ranges(Some(1..2))
12636        });
12637        editor.open_excerpts(&OpenExcerpts, window, cx);
12638    });
12639    cx.executor().run_until_parked();
12640    let first_item_id = workspace
12641        .update(cx, |workspace, window, cx| {
12642            let active_item = workspace
12643                .active_item(cx)
12644                .expect("should have an active item after navigating into the 1st buffer");
12645            let first_item_id = active_item.item_id();
12646            assert_ne!(
12647                first_item_id, multibuffer_item_id,
12648                "Should navigate into the 1st buffer and activate it"
12649            );
12650            assert!(
12651                active_item.is_singleton(cx),
12652                "New active item should be a singleton buffer"
12653            );
12654            assert_eq!(
12655                active_item
12656                    .act_as::<Editor>(cx)
12657                    .expect("should have navigated into an editor for the 1st buffer")
12658                    .read(cx)
12659                    .text(cx),
12660                sample_text_1
12661            );
12662
12663            workspace
12664                .go_back(workspace.active_pane().downgrade(), window, cx)
12665                .detach_and_log_err(cx);
12666
12667            first_item_id
12668        })
12669        .unwrap();
12670    cx.executor().run_until_parked();
12671    workspace
12672        .update(cx, |workspace, _, cx| {
12673            let active_item = workspace
12674                .active_item(cx)
12675                .expect("should have an active item after navigating back");
12676            assert_eq!(
12677                active_item.item_id(),
12678                multibuffer_item_id,
12679                "Should navigate back to the multi buffer"
12680            );
12681            assert!(!active_item.is_singleton(cx));
12682        })
12683        .unwrap();
12684
12685    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12686        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12687            s.select_ranges(Some(39..40))
12688        });
12689        editor.open_excerpts(&OpenExcerpts, window, cx);
12690    });
12691    cx.executor().run_until_parked();
12692    let second_item_id = workspace
12693        .update(cx, |workspace, window, cx| {
12694            let active_item = workspace
12695                .active_item(cx)
12696                .expect("should have an active item after navigating into the 2nd buffer");
12697            let second_item_id = active_item.item_id();
12698            assert_ne!(
12699                second_item_id, multibuffer_item_id,
12700                "Should navigate away from the multibuffer"
12701            );
12702            assert_ne!(
12703                second_item_id, first_item_id,
12704                "Should navigate into the 2nd buffer and activate it"
12705            );
12706            assert!(
12707                active_item.is_singleton(cx),
12708                "New active item should be a singleton buffer"
12709            );
12710            assert_eq!(
12711                active_item
12712                    .act_as::<Editor>(cx)
12713                    .expect("should have navigated into an editor")
12714                    .read(cx)
12715                    .text(cx),
12716                sample_text_2
12717            );
12718
12719            workspace
12720                .go_back(workspace.active_pane().downgrade(), window, cx)
12721                .detach_and_log_err(cx);
12722
12723            second_item_id
12724        })
12725        .unwrap();
12726    cx.executor().run_until_parked();
12727    workspace
12728        .update(cx, |workspace, _, cx| {
12729            let active_item = workspace
12730                .active_item(cx)
12731                .expect("should have an active item after navigating back from the 2nd buffer");
12732            assert_eq!(
12733                active_item.item_id(),
12734                multibuffer_item_id,
12735                "Should navigate back from the 2nd buffer to the multi buffer"
12736            );
12737            assert!(!active_item.is_singleton(cx));
12738        })
12739        .unwrap();
12740
12741    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12742        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12743            s.select_ranges(Some(70..70))
12744        });
12745        editor.open_excerpts(&OpenExcerpts, window, cx);
12746    });
12747    cx.executor().run_until_parked();
12748    workspace
12749        .update(cx, |workspace, window, cx| {
12750            let active_item = workspace
12751                .active_item(cx)
12752                .expect("should have an active item after navigating into the 3rd buffer");
12753            let third_item_id = active_item.item_id();
12754            assert_ne!(
12755                third_item_id, multibuffer_item_id,
12756                "Should navigate into the 3rd buffer and activate it"
12757            );
12758            assert_ne!(third_item_id, first_item_id);
12759            assert_ne!(third_item_id, second_item_id);
12760            assert!(
12761                active_item.is_singleton(cx),
12762                "New active item should be a singleton buffer"
12763            );
12764            assert_eq!(
12765                active_item
12766                    .act_as::<Editor>(cx)
12767                    .expect("should have navigated into an editor")
12768                    .read(cx)
12769                    .text(cx),
12770                sample_text_3
12771            );
12772
12773            workspace
12774                .go_back(workspace.active_pane().downgrade(), window, cx)
12775                .detach_and_log_err(cx);
12776        })
12777        .unwrap();
12778    cx.executor().run_until_parked();
12779    workspace
12780        .update(cx, |workspace, _, cx| {
12781            let active_item = workspace
12782                .active_item(cx)
12783                .expect("should have an active item after navigating back from the 3rd buffer");
12784            assert_eq!(
12785                active_item.item_id(),
12786                multibuffer_item_id,
12787                "Should navigate back from the 3rd buffer to the multi buffer"
12788            );
12789            assert!(!active_item.is_singleton(cx));
12790        })
12791        .unwrap();
12792}
12793
12794#[gpui::test]
12795async fn test_toggle_selected_diff_hunks(
12796    executor: BackgroundExecutor,
12797    cx: &mut gpui::TestAppContext,
12798) {
12799    init_test(cx, |_| {});
12800
12801    let mut cx = EditorTestContext::new(cx).await;
12802
12803    let diff_base = r#"
12804        use some::mod;
12805
12806        const A: u32 = 42;
12807
12808        fn main() {
12809            println!("hello");
12810
12811            println!("world");
12812        }
12813        "#
12814    .unindent();
12815
12816    cx.set_state(
12817        &r#"
12818        use some::modified;
12819
12820        ˇ
12821        fn main() {
12822            println!("hello there");
12823
12824            println!("around the");
12825            println!("world");
12826        }
12827        "#
12828        .unindent(),
12829    );
12830
12831    cx.set_diff_base(&diff_base);
12832    executor.run_until_parked();
12833
12834    cx.update_editor(|editor, window, cx| {
12835        editor.go_to_next_hunk(&GoToHunk, window, cx);
12836        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12837    });
12838    executor.run_until_parked();
12839    cx.assert_state_with_diff(
12840        r#"
12841          use some::modified;
12842
12843
12844          fn main() {
12845        -     println!("hello");
12846        + ˇ    println!("hello there");
12847
12848              println!("around the");
12849              println!("world");
12850          }
12851        "#
12852        .unindent(),
12853    );
12854
12855    cx.update_editor(|editor, window, cx| {
12856        for _ in 0..2 {
12857            editor.go_to_next_hunk(&GoToHunk, window, cx);
12858            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12859        }
12860    });
12861    executor.run_until_parked();
12862    cx.assert_state_with_diff(
12863        r#"
12864        - use some::mod;
12865        + ˇuse some::modified;
12866
12867
12868          fn main() {
12869        -     println!("hello");
12870        +     println!("hello there");
12871
12872        +     println!("around the");
12873              println!("world");
12874          }
12875        "#
12876        .unindent(),
12877    );
12878
12879    cx.update_editor(|editor, window, cx| {
12880        editor.go_to_next_hunk(&GoToHunk, window, cx);
12881        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12882    });
12883    executor.run_until_parked();
12884    cx.assert_state_with_diff(
12885        r#"
12886        - use some::mod;
12887        + use some::modified;
12888
12889        - const A: u32 = 42;
12890          ˇ
12891          fn main() {
12892        -     println!("hello");
12893        +     println!("hello there");
12894
12895        +     println!("around the");
12896              println!("world");
12897          }
12898        "#
12899        .unindent(),
12900    );
12901
12902    cx.update_editor(|editor, window, cx| {
12903        editor.cancel(&Cancel, window, cx);
12904    });
12905
12906    cx.assert_state_with_diff(
12907        r#"
12908          use some::modified;
12909
12910          ˇ
12911          fn main() {
12912              println!("hello there");
12913
12914              println!("around the");
12915              println!("world");
12916          }
12917        "#
12918        .unindent(),
12919    );
12920}
12921
12922#[gpui::test]
12923async fn test_diff_base_change_with_expanded_diff_hunks(
12924    executor: BackgroundExecutor,
12925    cx: &mut gpui::TestAppContext,
12926) {
12927    init_test(cx, |_| {});
12928
12929    let mut cx = EditorTestContext::new(cx).await;
12930
12931    let diff_base = r#"
12932        use some::mod1;
12933        use some::mod2;
12934
12935        const A: u32 = 42;
12936        const B: u32 = 42;
12937        const C: u32 = 42;
12938
12939        fn main() {
12940            println!("hello");
12941
12942            println!("world");
12943        }
12944        "#
12945    .unindent();
12946
12947    cx.set_state(
12948        &r#"
12949        use some::mod2;
12950
12951        const A: u32 = 42;
12952        const C: u32 = 42;
12953
12954        fn main(ˇ) {
12955            //println!("hello");
12956
12957            println!("world");
12958            //
12959            //
12960        }
12961        "#
12962        .unindent(),
12963    );
12964
12965    cx.set_diff_base(&diff_base);
12966    executor.run_until_parked();
12967
12968    cx.update_editor(|editor, window, cx| {
12969        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
12970    });
12971    executor.run_until_parked();
12972    cx.assert_state_with_diff(
12973        r#"
12974        - use some::mod1;
12975          use some::mod2;
12976
12977          const A: u32 = 42;
12978        - const B: u32 = 42;
12979          const C: u32 = 42;
12980
12981          fn main(ˇ) {
12982        -     println!("hello");
12983        +     //println!("hello");
12984
12985              println!("world");
12986        +     //
12987        +     //
12988          }
12989        "#
12990        .unindent(),
12991    );
12992
12993    cx.set_diff_base("new diff base!");
12994    executor.run_until_parked();
12995    cx.assert_state_with_diff(
12996        r#"
12997          use some::mod2;
12998
12999          const A: u32 = 42;
13000          const C: u32 = 42;
13001
13002          fn main(ˇ) {
13003              //println!("hello");
13004
13005              println!("world");
13006              //
13007              //
13008          }
13009        "#
13010        .unindent(),
13011    );
13012
13013    cx.update_editor(|editor, window, cx| {
13014        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13015    });
13016    executor.run_until_parked();
13017    cx.assert_state_with_diff(
13018        r#"
13019        - new diff base!
13020        + use some::mod2;
13021        +
13022        + const A: u32 = 42;
13023        + const C: u32 = 42;
13024        +
13025        + fn main(ˇ) {
13026        +     //println!("hello");
13027        +
13028        +     println!("world");
13029        +     //
13030        +     //
13031        + }
13032        "#
13033        .unindent(),
13034    );
13035}
13036
13037#[gpui::test]
13038async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13039    init_test(cx, |_| {});
13040
13041    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13042    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13043    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13044    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13045    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13046    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13047
13048    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13049    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13050    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13051
13052    let multi_buffer = cx.new(|cx| {
13053        let mut multibuffer = MultiBuffer::new(ReadWrite);
13054        multibuffer.push_excerpts(
13055            buffer_1.clone(),
13056            [
13057                ExcerptRange {
13058                    context: Point::new(0, 0)..Point::new(3, 0),
13059                    primary: None,
13060                },
13061                ExcerptRange {
13062                    context: Point::new(5, 0)..Point::new(7, 0),
13063                    primary: None,
13064                },
13065                ExcerptRange {
13066                    context: Point::new(9, 0)..Point::new(10, 3),
13067                    primary: None,
13068                },
13069            ],
13070            cx,
13071        );
13072        multibuffer.push_excerpts(
13073            buffer_2.clone(),
13074            [
13075                ExcerptRange {
13076                    context: Point::new(0, 0)..Point::new(3, 0),
13077                    primary: None,
13078                },
13079                ExcerptRange {
13080                    context: Point::new(5, 0)..Point::new(7, 0),
13081                    primary: None,
13082                },
13083                ExcerptRange {
13084                    context: Point::new(9, 0)..Point::new(10, 3),
13085                    primary: None,
13086                },
13087            ],
13088            cx,
13089        );
13090        multibuffer.push_excerpts(
13091            buffer_3.clone(),
13092            [
13093                ExcerptRange {
13094                    context: Point::new(0, 0)..Point::new(3, 0),
13095                    primary: None,
13096                },
13097                ExcerptRange {
13098                    context: Point::new(5, 0)..Point::new(7, 0),
13099                    primary: None,
13100                },
13101                ExcerptRange {
13102                    context: Point::new(9, 0)..Point::new(10, 3),
13103                    primary: None,
13104                },
13105            ],
13106            cx,
13107        );
13108        multibuffer
13109    });
13110
13111    let editor = cx.add_window(|window, cx| {
13112        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13113    });
13114    editor
13115        .update(cx, |editor, _window, cx| {
13116            for (buffer, diff_base) in [
13117                (buffer_1.clone(), file_1_old),
13118                (buffer_2.clone(), file_2_old),
13119                (buffer_3.clone(), file_3_old),
13120            ] {
13121                let change_set = cx.new(|cx| {
13122                    BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
13123                });
13124                editor
13125                    .buffer
13126                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13127            }
13128        })
13129        .unwrap();
13130
13131    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13132    cx.run_until_parked();
13133
13134    cx.assert_editor_state(
13135        &"
13136            ˇaaa
13137            ccc
13138            ddd
13139
13140            ggg
13141            hhh
13142
13143
13144            lll
13145            mmm
13146            NNN
13147
13148            qqq
13149            rrr
13150
13151            uuu
13152            111
13153            222
13154            333
13155
13156            666
13157            777
13158
13159            000
13160            !!!"
13161        .unindent(),
13162    );
13163
13164    cx.update_editor(|editor, window, cx| {
13165        editor.select_all(&SelectAll, window, cx);
13166        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13167    });
13168    cx.executor().run_until_parked();
13169
13170    cx.assert_state_with_diff(
13171        "
13172            «aaa
13173          - bbb
13174            ccc
13175            ddd
13176
13177            ggg
13178            hhh
13179
13180
13181            lll
13182            mmm
13183          - nnn
13184          + NNN
13185
13186            qqq
13187            rrr
13188
13189            uuu
13190            111
13191            222
13192            333
13193
13194          + 666
13195            777
13196
13197            000
13198            !!!ˇ»"
13199            .unindent(),
13200    );
13201}
13202
13203#[gpui::test]
13204async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13205    init_test(cx, |_| {});
13206
13207    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13208    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
13209
13210    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13211    let multi_buffer = cx.new(|cx| {
13212        let mut multibuffer = MultiBuffer::new(ReadWrite);
13213        multibuffer.push_excerpts(
13214            buffer.clone(),
13215            [
13216                ExcerptRange {
13217                    context: Point::new(0, 0)..Point::new(2, 0),
13218                    primary: None,
13219                },
13220                ExcerptRange {
13221                    context: Point::new(5, 0)..Point::new(7, 0),
13222                    primary: None,
13223                },
13224            ],
13225            cx,
13226        );
13227        multibuffer
13228    });
13229
13230    let editor = cx.add_window(|window, cx| {
13231        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13232    });
13233    editor
13234        .update(cx, |editor, _window, cx| {
13235            let change_set =
13236                cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
13237            editor
13238                .buffer
13239                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13240        })
13241        .unwrap();
13242
13243    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13244    cx.run_until_parked();
13245
13246    cx.update_editor(|editor, window, cx| {
13247        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13248    });
13249    cx.executor().run_until_parked();
13250
13251    cx.assert_state_with_diff(
13252        "
13253            ˇaaa
13254          - bbb
13255          + BBB
13256
13257          + EEE
13258            fff
13259        "
13260        .unindent(),
13261    );
13262}
13263
13264#[gpui::test]
13265async fn test_edits_around_expanded_insertion_hunks(
13266    executor: BackgroundExecutor,
13267    cx: &mut gpui::TestAppContext,
13268) {
13269    init_test(cx, |_| {});
13270
13271    let mut cx = EditorTestContext::new(cx).await;
13272
13273    let diff_base = r#"
13274        use some::mod1;
13275        use some::mod2;
13276
13277        const A: u32 = 42;
13278
13279        fn main() {
13280            println!("hello");
13281
13282            println!("world");
13283        }
13284        "#
13285    .unindent();
13286    executor.run_until_parked();
13287    cx.set_state(
13288        &r#"
13289        use some::mod1;
13290        use some::mod2;
13291
13292        const A: u32 = 42;
13293        const B: u32 = 42;
13294        const C: u32 = 42;
13295        ˇ
13296
13297        fn main() {
13298            println!("hello");
13299
13300            println!("world");
13301        }
13302        "#
13303        .unindent(),
13304    );
13305
13306    cx.set_diff_base(&diff_base);
13307    executor.run_until_parked();
13308
13309    cx.update_editor(|editor, window, cx| {
13310        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13311    });
13312    executor.run_until_parked();
13313
13314    cx.assert_state_with_diff(
13315        r#"
13316        use some::mod1;
13317        use some::mod2;
13318
13319        const A: u32 = 42;
13320      + const B: u32 = 42;
13321      + const C: u32 = 42;
13322      + ˇ
13323
13324        fn main() {
13325            println!("hello");
13326
13327            println!("world");
13328        }
13329      "#
13330        .unindent(),
13331    );
13332
13333    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13334    executor.run_until_parked();
13335
13336    cx.assert_state_with_diff(
13337        r#"
13338        use some::mod1;
13339        use some::mod2;
13340
13341        const A: u32 = 42;
13342      + const B: u32 = 42;
13343      + const C: u32 = 42;
13344      + const D: u32 = 42;
13345      + ˇ
13346
13347        fn main() {
13348            println!("hello");
13349
13350            println!("world");
13351        }
13352      "#
13353        .unindent(),
13354    );
13355
13356    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13357    executor.run_until_parked();
13358
13359    cx.assert_state_with_diff(
13360        r#"
13361        use some::mod1;
13362        use some::mod2;
13363
13364        const A: u32 = 42;
13365      + const B: u32 = 42;
13366      + const C: u32 = 42;
13367      + const D: u32 = 42;
13368      + const E: u32 = 42;
13369      + ˇ
13370
13371        fn main() {
13372            println!("hello");
13373
13374            println!("world");
13375        }
13376      "#
13377        .unindent(),
13378    );
13379
13380    cx.update_editor(|editor, window, cx| {
13381        editor.delete_line(&DeleteLine, window, cx);
13382    });
13383    executor.run_until_parked();
13384
13385    cx.assert_state_with_diff(
13386        r#"
13387        use some::mod1;
13388        use some::mod2;
13389
13390        const A: u32 = 42;
13391      + const B: u32 = 42;
13392      + const C: u32 = 42;
13393      + const D: u32 = 42;
13394      + const E: u32 = 42;
13395        ˇ
13396        fn main() {
13397            println!("hello");
13398
13399            println!("world");
13400        }
13401      "#
13402        .unindent(),
13403    );
13404
13405    cx.update_editor(|editor, window, cx| {
13406        editor.move_up(&MoveUp, window, cx);
13407        editor.delete_line(&DeleteLine, window, cx);
13408        editor.move_up(&MoveUp, window, cx);
13409        editor.delete_line(&DeleteLine, window, cx);
13410        editor.move_up(&MoveUp, window, cx);
13411        editor.delete_line(&DeleteLine, window, cx);
13412    });
13413    executor.run_until_parked();
13414    cx.assert_state_with_diff(
13415        r#"
13416        use some::mod1;
13417        use some::mod2;
13418
13419        const A: u32 = 42;
13420      + const B: u32 = 42;
13421        ˇ
13422        fn main() {
13423            println!("hello");
13424
13425            println!("world");
13426        }
13427      "#
13428        .unindent(),
13429    );
13430
13431    cx.update_editor(|editor, window, cx| {
13432        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13433        editor.delete_line(&DeleteLine, window, cx);
13434    });
13435    executor.run_until_parked();
13436    cx.assert_state_with_diff(
13437        r#"
13438        ˇ
13439        fn main() {
13440            println!("hello");
13441
13442            println!("world");
13443        }
13444      "#
13445        .unindent(),
13446    );
13447}
13448
13449#[gpui::test]
13450async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13451    init_test(cx, |_| {});
13452
13453    let mut cx = EditorTestContext::new(cx).await;
13454    cx.set_diff_base(indoc! { "
13455        one
13456        two
13457        three
13458        four
13459        five
13460        "
13461    });
13462    cx.set_state(indoc! { "
13463        one
13464        ˇthree
13465        five
13466    "});
13467    cx.run_until_parked();
13468    cx.update_editor(|editor, window, cx| {
13469        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13470    });
13471    cx.assert_state_with_diff(
13472        indoc! { "
13473        one
13474      - two
13475        ˇthree
13476      - four
13477        five
13478    "}
13479        .to_string(),
13480    );
13481    cx.update_editor(|editor, window, cx| {
13482        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13483    });
13484
13485    cx.assert_state_with_diff(
13486        indoc! { "
13487        one
13488        ˇthree
13489        five
13490    "}
13491        .to_string(),
13492    );
13493
13494    cx.set_state(indoc! { "
13495        one
13496        TWO
13497        ˇthree
13498        four
13499        five
13500    "});
13501    cx.run_until_parked();
13502    cx.update_editor(|editor, window, cx| {
13503        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13504    });
13505
13506    cx.assert_state_with_diff(
13507        indoc! { "
13508            one
13509          - two
13510          + TWO
13511            ˇthree
13512            four
13513            five
13514        "}
13515        .to_string(),
13516    );
13517    cx.update_editor(|editor, window, cx| {
13518        editor.move_up(&Default::default(), window, cx);
13519        editor.move_up(&Default::default(), window, cx);
13520        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13521    });
13522    cx.assert_state_with_diff(
13523        indoc! { "
13524            one
13525            ˇTWO
13526            three
13527            four
13528            five
13529        "}
13530        .to_string(),
13531    );
13532}
13533
13534#[gpui::test]
13535async fn test_edits_around_expanded_deletion_hunks(
13536    executor: BackgroundExecutor,
13537    cx: &mut gpui::TestAppContext,
13538) {
13539    init_test(cx, |_| {});
13540
13541    let mut cx = EditorTestContext::new(cx).await;
13542
13543    let diff_base = r#"
13544        use some::mod1;
13545        use some::mod2;
13546
13547        const A: u32 = 42;
13548        const B: u32 = 42;
13549        const C: u32 = 42;
13550
13551
13552        fn main() {
13553            println!("hello");
13554
13555            println!("world");
13556        }
13557    "#
13558    .unindent();
13559    executor.run_until_parked();
13560    cx.set_state(
13561        &r#"
13562        use some::mod1;
13563        use some::mod2;
13564
13565        ˇconst B: u32 = 42;
13566        const C: u32 = 42;
13567
13568
13569        fn main() {
13570            println!("hello");
13571
13572            println!("world");
13573        }
13574        "#
13575        .unindent(),
13576    );
13577
13578    cx.set_diff_base(&diff_base);
13579    executor.run_until_parked();
13580
13581    cx.update_editor(|editor, window, cx| {
13582        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13583    });
13584    executor.run_until_parked();
13585
13586    cx.assert_state_with_diff(
13587        r#"
13588        use some::mod1;
13589        use some::mod2;
13590
13591      - const A: u32 = 42;
13592        ˇconst B: u32 = 42;
13593        const C: u32 = 42;
13594
13595
13596        fn main() {
13597            println!("hello");
13598
13599            println!("world");
13600        }
13601      "#
13602        .unindent(),
13603    );
13604
13605    cx.update_editor(|editor, window, cx| {
13606        editor.delete_line(&DeleteLine, window, cx);
13607    });
13608    executor.run_until_parked();
13609    cx.assert_state_with_diff(
13610        r#"
13611        use some::mod1;
13612        use some::mod2;
13613
13614      - const A: u32 = 42;
13615      - const B: u32 = 42;
13616        ˇconst C: u32 = 42;
13617
13618
13619        fn main() {
13620            println!("hello");
13621
13622            println!("world");
13623        }
13624      "#
13625        .unindent(),
13626    );
13627
13628    cx.update_editor(|editor, window, cx| {
13629        editor.delete_line(&DeleteLine, window, cx);
13630    });
13631    executor.run_until_parked();
13632    cx.assert_state_with_diff(
13633        r#"
13634        use some::mod1;
13635        use some::mod2;
13636
13637      - const A: u32 = 42;
13638      - const B: u32 = 42;
13639      - const C: u32 = 42;
13640        ˇ
13641
13642        fn main() {
13643            println!("hello");
13644
13645            println!("world");
13646        }
13647      "#
13648        .unindent(),
13649    );
13650
13651    cx.update_editor(|editor, window, cx| {
13652        editor.handle_input("replacement", window, cx);
13653    });
13654    executor.run_until_parked();
13655    cx.assert_state_with_diff(
13656        r#"
13657        use some::mod1;
13658        use some::mod2;
13659
13660      - const A: u32 = 42;
13661      - const B: u32 = 42;
13662      - const C: u32 = 42;
13663      -
13664      + replacementˇ
13665
13666        fn main() {
13667            println!("hello");
13668
13669            println!("world");
13670        }
13671      "#
13672        .unindent(),
13673    );
13674}
13675
13676#[gpui::test]
13677async fn test_backspace_after_deletion_hunk(
13678    executor: BackgroundExecutor,
13679    cx: &mut gpui::TestAppContext,
13680) {
13681    init_test(cx, |_| {});
13682
13683    let mut cx = EditorTestContext::new(cx).await;
13684
13685    let base_text = r#"
13686        one
13687        two
13688        three
13689        four
13690        five
13691    "#
13692    .unindent();
13693    executor.run_until_parked();
13694    cx.set_state(
13695        &r#"
13696        one
13697        two
13698        fˇour
13699        five
13700        "#
13701        .unindent(),
13702    );
13703
13704    cx.set_diff_base(&base_text);
13705    executor.run_until_parked();
13706
13707    cx.update_editor(|editor, window, cx| {
13708        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13709    });
13710    executor.run_until_parked();
13711
13712    cx.assert_state_with_diff(
13713        r#"
13714          one
13715          two
13716        - three
13717          fˇour
13718          five
13719        "#
13720        .unindent(),
13721    );
13722
13723    cx.update_editor(|editor, window, cx| {
13724        editor.backspace(&Backspace, window, cx);
13725        editor.backspace(&Backspace, window, cx);
13726    });
13727    executor.run_until_parked();
13728    cx.assert_state_with_diff(
13729        r#"
13730          one
13731          two
13732        - threeˇ
13733        - four
13734        + our
13735          five
13736        "#
13737        .unindent(),
13738    );
13739}
13740
13741#[gpui::test]
13742async fn test_edit_after_expanded_modification_hunk(
13743    executor: BackgroundExecutor,
13744    cx: &mut gpui::TestAppContext,
13745) {
13746    init_test(cx, |_| {});
13747
13748    let mut cx = EditorTestContext::new(cx).await;
13749
13750    let diff_base = r#"
13751        use some::mod1;
13752        use some::mod2;
13753
13754        const A: u32 = 42;
13755        const B: u32 = 42;
13756        const C: u32 = 42;
13757        const D: u32 = 42;
13758
13759
13760        fn main() {
13761            println!("hello");
13762
13763            println!("world");
13764        }"#
13765    .unindent();
13766
13767    cx.set_state(
13768        &r#"
13769        use some::mod1;
13770        use some::mod2;
13771
13772        const A: u32 = 42;
13773        const B: u32 = 42;
13774        const C: u32 = 43ˇ
13775        const D: u32 = 42;
13776
13777
13778        fn main() {
13779            println!("hello");
13780
13781            println!("world");
13782        }"#
13783        .unindent(),
13784    );
13785
13786    cx.set_diff_base(&diff_base);
13787    executor.run_until_parked();
13788    cx.update_editor(|editor, window, cx| {
13789        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13790    });
13791    executor.run_until_parked();
13792
13793    cx.assert_state_with_diff(
13794        r#"
13795        use some::mod1;
13796        use some::mod2;
13797
13798        const A: u32 = 42;
13799        const B: u32 = 42;
13800      - const C: u32 = 42;
13801      + const C: u32 = 43ˇ
13802        const D: u32 = 42;
13803
13804
13805        fn main() {
13806            println!("hello");
13807
13808            println!("world");
13809        }"#
13810        .unindent(),
13811    );
13812
13813    cx.update_editor(|editor, window, cx| {
13814        editor.handle_input("\nnew_line\n", window, cx);
13815    });
13816    executor.run_until_parked();
13817
13818    cx.assert_state_with_diff(
13819        r#"
13820        use some::mod1;
13821        use some::mod2;
13822
13823        const A: u32 = 42;
13824        const B: u32 = 42;
13825      - const C: u32 = 42;
13826      + const C: u32 = 43
13827      + new_line
13828      + ˇ
13829        const D: u32 = 42;
13830
13831
13832        fn main() {
13833            println!("hello");
13834
13835            println!("world");
13836        }"#
13837        .unindent(),
13838    );
13839}
13840
13841async fn setup_indent_guides_editor(
13842    text: &str,
13843    cx: &mut gpui::TestAppContext,
13844) -> (BufferId, EditorTestContext) {
13845    init_test(cx, |_| {});
13846
13847    let mut cx = EditorTestContext::new(cx).await;
13848
13849    let buffer_id = cx.update_editor(|editor, window, cx| {
13850        editor.set_text(text, window, cx);
13851        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13852
13853        buffer_ids[0]
13854    });
13855
13856    (buffer_id, cx)
13857}
13858
13859fn assert_indent_guides(
13860    range: Range<u32>,
13861    expected: Vec<IndentGuide>,
13862    active_indices: Option<Vec<usize>>,
13863    cx: &mut EditorTestContext,
13864) {
13865    let indent_guides = cx.update_editor(|editor, window, cx| {
13866        let snapshot = editor.snapshot(window, cx).display_snapshot;
13867        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13868            editor,
13869            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13870            true,
13871            &snapshot,
13872            cx,
13873        );
13874
13875        indent_guides.sort_by(|a, b| {
13876            a.depth.cmp(&b.depth).then(
13877                a.start_row
13878                    .cmp(&b.start_row)
13879                    .then(a.end_row.cmp(&b.end_row)),
13880            )
13881        });
13882        indent_guides
13883    });
13884
13885    if let Some(expected) = active_indices {
13886        let active_indices = cx.update_editor(|editor, window, cx| {
13887            let snapshot = editor.snapshot(window, cx).display_snapshot;
13888            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13889        });
13890
13891        assert_eq!(
13892            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13893            expected,
13894            "Active indent guide indices do not match"
13895        );
13896    }
13897
13898    assert_eq!(indent_guides, expected, "Indent guides do not match");
13899}
13900
13901fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13902    IndentGuide {
13903        buffer_id,
13904        start_row: MultiBufferRow(start_row),
13905        end_row: MultiBufferRow(end_row),
13906        depth,
13907        tab_size: 4,
13908        settings: IndentGuideSettings {
13909            enabled: true,
13910            line_width: 1,
13911            active_line_width: 1,
13912            ..Default::default()
13913        },
13914    }
13915}
13916
13917#[gpui::test]
13918async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13919    let (buffer_id, mut cx) = setup_indent_guides_editor(
13920        &"
13921    fn main() {
13922        let a = 1;
13923    }"
13924        .unindent(),
13925        cx,
13926    )
13927    .await;
13928
13929    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13930}
13931
13932#[gpui::test]
13933async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13934    let (buffer_id, mut cx) = setup_indent_guides_editor(
13935        &"
13936    fn main() {
13937        let a = 1;
13938        let b = 2;
13939    }"
13940        .unindent(),
13941        cx,
13942    )
13943    .await;
13944
13945    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13946}
13947
13948#[gpui::test]
13949async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13950    let (buffer_id, mut cx) = setup_indent_guides_editor(
13951        &"
13952    fn main() {
13953        let a = 1;
13954        if a == 3 {
13955            let b = 2;
13956        } else {
13957            let c = 3;
13958        }
13959    }"
13960        .unindent(),
13961        cx,
13962    )
13963    .await;
13964
13965    assert_indent_guides(
13966        0..8,
13967        vec![
13968            indent_guide(buffer_id, 1, 6, 0),
13969            indent_guide(buffer_id, 3, 3, 1),
13970            indent_guide(buffer_id, 5, 5, 1),
13971        ],
13972        None,
13973        &mut cx,
13974    );
13975}
13976
13977#[gpui::test]
13978async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13979    let (buffer_id, mut cx) = setup_indent_guides_editor(
13980        &"
13981    fn main() {
13982        let a = 1;
13983            let b = 2;
13984        let c = 3;
13985    }"
13986        .unindent(),
13987        cx,
13988    )
13989    .await;
13990
13991    assert_indent_guides(
13992        0..5,
13993        vec![
13994            indent_guide(buffer_id, 1, 3, 0),
13995            indent_guide(buffer_id, 2, 2, 1),
13996        ],
13997        None,
13998        &mut cx,
13999    );
14000}
14001
14002#[gpui::test]
14003async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14004    let (buffer_id, mut cx) = setup_indent_guides_editor(
14005        &"
14006        fn main() {
14007            let a = 1;
14008
14009            let c = 3;
14010        }"
14011        .unindent(),
14012        cx,
14013    )
14014    .await;
14015
14016    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14017}
14018
14019#[gpui::test]
14020async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14021    let (buffer_id, mut cx) = setup_indent_guides_editor(
14022        &"
14023        fn main() {
14024            let a = 1;
14025
14026            let c = 3;
14027
14028            if a == 3 {
14029                let b = 2;
14030            } else {
14031                let c = 3;
14032            }
14033        }"
14034        .unindent(),
14035        cx,
14036    )
14037    .await;
14038
14039    assert_indent_guides(
14040        0..11,
14041        vec![
14042            indent_guide(buffer_id, 1, 9, 0),
14043            indent_guide(buffer_id, 6, 6, 1),
14044            indent_guide(buffer_id, 8, 8, 1),
14045        ],
14046        None,
14047        &mut cx,
14048    );
14049}
14050
14051#[gpui::test]
14052async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14053    let (buffer_id, mut cx) = setup_indent_guides_editor(
14054        &"
14055        fn main() {
14056            let a = 1;
14057
14058            let c = 3;
14059
14060            if a == 3 {
14061                let b = 2;
14062            } else {
14063                let c = 3;
14064            }
14065        }"
14066        .unindent(),
14067        cx,
14068    )
14069    .await;
14070
14071    assert_indent_guides(
14072        1..11,
14073        vec![
14074            indent_guide(buffer_id, 1, 9, 0),
14075            indent_guide(buffer_id, 6, 6, 1),
14076            indent_guide(buffer_id, 8, 8, 1),
14077        ],
14078        None,
14079        &mut cx,
14080    );
14081}
14082
14083#[gpui::test]
14084async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14085    let (buffer_id, mut cx) = setup_indent_guides_editor(
14086        &"
14087        fn main() {
14088            let a = 1;
14089
14090            let c = 3;
14091
14092            if a == 3 {
14093                let b = 2;
14094            } else {
14095                let c = 3;
14096            }
14097        }"
14098        .unindent(),
14099        cx,
14100    )
14101    .await;
14102
14103    assert_indent_guides(
14104        1..10,
14105        vec![
14106            indent_guide(buffer_id, 1, 9, 0),
14107            indent_guide(buffer_id, 6, 6, 1),
14108            indent_guide(buffer_id, 8, 8, 1),
14109        ],
14110        None,
14111        &mut cx,
14112    );
14113}
14114
14115#[gpui::test]
14116async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14117    let (buffer_id, mut cx) = setup_indent_guides_editor(
14118        &"
14119        block1
14120            block2
14121                block3
14122                    block4
14123            block2
14124        block1
14125        block1"
14126            .unindent(),
14127        cx,
14128    )
14129    .await;
14130
14131    assert_indent_guides(
14132        1..10,
14133        vec![
14134            indent_guide(buffer_id, 1, 4, 0),
14135            indent_guide(buffer_id, 2, 3, 1),
14136            indent_guide(buffer_id, 3, 3, 2),
14137        ],
14138        None,
14139        &mut cx,
14140    );
14141}
14142
14143#[gpui::test]
14144async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14145    let (buffer_id, mut cx) = setup_indent_guides_editor(
14146        &"
14147        block1
14148            block2
14149                block3
14150
14151        block1
14152        block1"
14153            .unindent(),
14154        cx,
14155    )
14156    .await;
14157
14158    assert_indent_guides(
14159        0..6,
14160        vec![
14161            indent_guide(buffer_id, 1, 2, 0),
14162            indent_guide(buffer_id, 2, 2, 1),
14163        ],
14164        None,
14165        &mut cx,
14166    );
14167}
14168
14169#[gpui::test]
14170async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14171    let (buffer_id, mut cx) = setup_indent_guides_editor(
14172        &"
14173        block1
14174
14175
14176
14177            block2
14178        "
14179        .unindent(),
14180        cx,
14181    )
14182    .await;
14183
14184    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14185}
14186
14187#[gpui::test]
14188async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14189    let (buffer_id, mut cx) = setup_indent_guides_editor(
14190        &"
14191        def a:
14192        \tb = 3
14193        \tif True:
14194        \t\tc = 4
14195        \t\td = 5
14196        \tprint(b)
14197        "
14198        .unindent(),
14199        cx,
14200    )
14201    .await;
14202
14203    assert_indent_guides(
14204        0..6,
14205        vec![
14206            indent_guide(buffer_id, 1, 6, 0),
14207            indent_guide(buffer_id, 3, 4, 1),
14208        ],
14209        None,
14210        &mut cx,
14211    );
14212}
14213
14214#[gpui::test]
14215async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14216    let (buffer_id, mut cx) = setup_indent_guides_editor(
14217        &"
14218    fn main() {
14219        let a = 1;
14220    }"
14221        .unindent(),
14222        cx,
14223    )
14224    .await;
14225
14226    cx.update_editor(|editor, window, cx| {
14227        editor.change_selections(None, window, cx, |s| {
14228            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14229        });
14230    });
14231
14232    assert_indent_guides(
14233        0..3,
14234        vec![indent_guide(buffer_id, 1, 1, 0)],
14235        Some(vec![0]),
14236        &mut cx,
14237    );
14238}
14239
14240#[gpui::test]
14241async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14242    let (buffer_id, mut cx) = setup_indent_guides_editor(
14243        &"
14244    fn main() {
14245        if 1 == 2 {
14246            let a = 1;
14247        }
14248    }"
14249        .unindent(),
14250        cx,
14251    )
14252    .await;
14253
14254    cx.update_editor(|editor, window, cx| {
14255        editor.change_selections(None, window, cx, |s| {
14256            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14257        });
14258    });
14259
14260    assert_indent_guides(
14261        0..4,
14262        vec![
14263            indent_guide(buffer_id, 1, 3, 0),
14264            indent_guide(buffer_id, 2, 2, 1),
14265        ],
14266        Some(vec![1]),
14267        &mut cx,
14268    );
14269
14270    cx.update_editor(|editor, window, cx| {
14271        editor.change_selections(None, window, cx, |s| {
14272            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14273        });
14274    });
14275
14276    assert_indent_guides(
14277        0..4,
14278        vec![
14279            indent_guide(buffer_id, 1, 3, 0),
14280            indent_guide(buffer_id, 2, 2, 1),
14281        ],
14282        Some(vec![1]),
14283        &mut cx,
14284    );
14285
14286    cx.update_editor(|editor, window, cx| {
14287        editor.change_selections(None, window, cx, |s| {
14288            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14289        });
14290    });
14291
14292    assert_indent_guides(
14293        0..4,
14294        vec![
14295            indent_guide(buffer_id, 1, 3, 0),
14296            indent_guide(buffer_id, 2, 2, 1),
14297        ],
14298        Some(vec![0]),
14299        &mut cx,
14300    );
14301}
14302
14303#[gpui::test]
14304async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14305    let (buffer_id, mut cx) = setup_indent_guides_editor(
14306        &"
14307    fn main() {
14308        let a = 1;
14309
14310        let b = 2;
14311    }"
14312        .unindent(),
14313        cx,
14314    )
14315    .await;
14316
14317    cx.update_editor(|editor, window, cx| {
14318        editor.change_selections(None, window, cx, |s| {
14319            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14320        });
14321    });
14322
14323    assert_indent_guides(
14324        0..5,
14325        vec![indent_guide(buffer_id, 1, 3, 0)],
14326        Some(vec![0]),
14327        &mut cx,
14328    );
14329}
14330
14331#[gpui::test]
14332async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14333    let (buffer_id, mut cx) = setup_indent_guides_editor(
14334        &"
14335    def m:
14336        a = 1
14337        pass"
14338            .unindent(),
14339        cx,
14340    )
14341    .await;
14342
14343    cx.update_editor(|editor, window, cx| {
14344        editor.change_selections(None, window, cx, |s| {
14345            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14346        });
14347    });
14348
14349    assert_indent_guides(
14350        0..3,
14351        vec![indent_guide(buffer_id, 1, 2, 0)],
14352        Some(vec![0]),
14353        &mut cx,
14354    );
14355}
14356
14357#[gpui::test]
14358async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14359    init_test(cx, |_| {});
14360    let mut cx = EditorTestContext::new(cx).await;
14361    let text = indoc! {
14362        "
14363        impl A {
14364            fn b() {
14365                0;
14366                3;
14367                5;
14368                6;
14369                7;
14370            }
14371        }
14372        "
14373    };
14374    let base_text = indoc! {
14375        "
14376        impl A {
14377            fn b() {
14378                0;
14379                1;
14380                2;
14381                3;
14382                4;
14383            }
14384            fn c() {
14385                5;
14386                6;
14387                7;
14388            }
14389        }
14390        "
14391    };
14392
14393    cx.update_editor(|editor, window, cx| {
14394        editor.set_text(text, window, cx);
14395
14396        editor.buffer().update(cx, |multibuffer, cx| {
14397            let buffer = multibuffer.as_singleton().unwrap();
14398            let change_set = cx.new(|cx| {
14399                let mut change_set = BufferChangeSet::new(&buffer, cx);
14400                let _ =
14401                    change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
14402                change_set
14403            });
14404
14405            multibuffer.set_all_diff_hunks_expanded(cx);
14406            multibuffer.add_change_set(change_set, cx);
14407
14408            buffer.read(cx).remote_id()
14409        })
14410    });
14411    cx.run_until_parked();
14412
14413    cx.assert_state_with_diff(
14414        indoc! { "
14415          impl A {
14416              fn b() {
14417                  0;
14418        -         1;
14419        -         2;
14420                  3;
14421        -         4;
14422        -     }
14423        -     fn c() {
14424                  5;
14425                  6;
14426                  7;
14427              }
14428          }
14429          ˇ"
14430        }
14431        .to_string(),
14432    );
14433
14434    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14435        editor
14436            .snapshot(window, cx)
14437            .buffer_snapshot
14438            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14439            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14440            .collect::<Vec<_>>()
14441    });
14442    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14443    assert_eq!(
14444        actual_guides,
14445        vec![
14446            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14447            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14448            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14449        ]
14450    );
14451}
14452
14453#[gpui::test]
14454fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14455    init_test(cx, |_| {});
14456
14457    let editor = cx.add_window(|window, cx| {
14458        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14459        build_editor(buffer, window, cx)
14460    });
14461
14462    let render_args = Arc::new(Mutex::new(None));
14463    let snapshot = editor
14464        .update(cx, |editor, window, cx| {
14465            let snapshot = editor.buffer().read(cx).snapshot(cx);
14466            let range =
14467                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14468
14469            struct RenderArgs {
14470                row: MultiBufferRow,
14471                folded: bool,
14472                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14473            }
14474
14475            let crease = Crease::inline(
14476                range,
14477                FoldPlaceholder::test(),
14478                {
14479                    let toggle_callback = render_args.clone();
14480                    move |row, folded, callback, _window, _cx| {
14481                        *toggle_callback.lock() = Some(RenderArgs {
14482                            row,
14483                            folded,
14484                            callback,
14485                        });
14486                        div()
14487                    }
14488                },
14489                |_row, _folded, _window, _cx| div(),
14490            );
14491
14492            editor.insert_creases(Some(crease), cx);
14493            let snapshot = editor.snapshot(window, cx);
14494            let _div = snapshot.render_crease_toggle(
14495                MultiBufferRow(1),
14496                false,
14497                cx.entity().clone(),
14498                window,
14499                cx,
14500            );
14501            snapshot
14502        })
14503        .unwrap();
14504
14505    let render_args = render_args.lock().take().unwrap();
14506    assert_eq!(render_args.row, MultiBufferRow(1));
14507    assert!(!render_args.folded);
14508    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14509
14510    cx.update_window(*editor, |_, window, cx| {
14511        (render_args.callback)(true, window, cx)
14512    })
14513    .unwrap();
14514    let snapshot = editor
14515        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14516        .unwrap();
14517    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14518
14519    cx.update_window(*editor, |_, window, cx| {
14520        (render_args.callback)(false, window, cx)
14521    })
14522    .unwrap();
14523    let snapshot = editor
14524        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14525        .unwrap();
14526    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14527}
14528
14529#[gpui::test]
14530async fn test_input_text(cx: &mut gpui::TestAppContext) {
14531    init_test(cx, |_| {});
14532    let mut cx = EditorTestContext::new(cx).await;
14533
14534    cx.set_state(
14535        &r#"ˇone
14536        two
14537
14538        three
14539        fourˇ
14540        five
14541
14542        siˇx"#
14543            .unindent(),
14544    );
14545
14546    cx.dispatch_action(HandleInput(String::new()));
14547    cx.assert_editor_state(
14548        &r#"ˇone
14549        two
14550
14551        three
14552        fourˇ
14553        five
14554
14555        siˇx"#
14556            .unindent(),
14557    );
14558
14559    cx.dispatch_action(HandleInput("AAAA".to_string()));
14560    cx.assert_editor_state(
14561        &r#"AAAAˇone
14562        two
14563
14564        three
14565        fourAAAAˇ
14566        five
14567
14568        siAAAAˇx"#
14569            .unindent(),
14570    );
14571}
14572
14573#[gpui::test]
14574async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14575    init_test(cx, |_| {});
14576
14577    let mut cx = EditorTestContext::new(cx).await;
14578    cx.set_state(
14579        r#"let foo = 1;
14580let foo = 2;
14581let foo = 3;
14582let fooˇ = 4;
14583let foo = 5;
14584let foo = 6;
14585let foo = 7;
14586let foo = 8;
14587let foo = 9;
14588let foo = 10;
14589let foo = 11;
14590let foo = 12;
14591let foo = 13;
14592let foo = 14;
14593let foo = 15;"#,
14594    );
14595
14596    cx.update_editor(|e, window, cx| {
14597        assert_eq!(
14598            e.next_scroll_position,
14599            NextScrollCursorCenterTopBottom::Center,
14600            "Default next scroll direction is center",
14601        );
14602
14603        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14604        assert_eq!(
14605            e.next_scroll_position,
14606            NextScrollCursorCenterTopBottom::Top,
14607            "After center, next scroll direction should be top",
14608        );
14609
14610        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14611        assert_eq!(
14612            e.next_scroll_position,
14613            NextScrollCursorCenterTopBottom::Bottom,
14614            "After top, next scroll direction should be bottom",
14615        );
14616
14617        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14618        assert_eq!(
14619            e.next_scroll_position,
14620            NextScrollCursorCenterTopBottom::Center,
14621            "After bottom, scrolling should start over",
14622        );
14623
14624        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14625        assert_eq!(
14626            e.next_scroll_position,
14627            NextScrollCursorCenterTopBottom::Top,
14628            "Scrolling continues if retriggered fast enough"
14629        );
14630    });
14631
14632    cx.executor()
14633        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14634    cx.executor().run_until_parked();
14635    cx.update_editor(|e, _, _| {
14636        assert_eq!(
14637            e.next_scroll_position,
14638            NextScrollCursorCenterTopBottom::Center,
14639            "If scrolling is not triggered fast enough, it should reset"
14640        );
14641    });
14642}
14643
14644#[gpui::test]
14645async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14646    init_test(cx, |_| {});
14647    let mut cx = EditorLspTestContext::new_rust(
14648        lsp::ServerCapabilities {
14649            definition_provider: Some(lsp::OneOf::Left(true)),
14650            references_provider: Some(lsp::OneOf::Left(true)),
14651            ..lsp::ServerCapabilities::default()
14652        },
14653        cx,
14654    )
14655    .await;
14656
14657    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14658        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14659            move |params, _| async move {
14660                if empty_go_to_definition {
14661                    Ok(None)
14662                } else {
14663                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14664                        uri: params.text_document_position_params.text_document.uri,
14665                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14666                    })))
14667                }
14668            },
14669        );
14670        let references =
14671            cx.lsp
14672                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14673                    Ok(Some(vec![lsp::Location {
14674                        uri: params.text_document_position.text_document.uri,
14675                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14676                    }]))
14677                });
14678        (go_to_definition, references)
14679    };
14680
14681    cx.set_state(
14682        &r#"fn one() {
14683            let mut a = ˇtwo();
14684        }
14685
14686        fn two() {}"#
14687            .unindent(),
14688    );
14689    set_up_lsp_handlers(false, &mut cx);
14690    let navigated = cx
14691        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14692        .await
14693        .expect("Failed to navigate to definition");
14694    assert_eq!(
14695        navigated,
14696        Navigated::Yes,
14697        "Should have navigated to definition from the GetDefinition response"
14698    );
14699    cx.assert_editor_state(
14700        &r#"fn one() {
14701            let mut a = two();
14702        }
14703
14704        fn «twoˇ»() {}"#
14705            .unindent(),
14706    );
14707
14708    let editors = cx.update_workspace(|workspace, _, cx| {
14709        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14710    });
14711    cx.update_editor(|_, _, test_editor_cx| {
14712        assert_eq!(
14713            editors.len(),
14714            1,
14715            "Initially, only one, test, editor should be open in the workspace"
14716        );
14717        assert_eq!(
14718            test_editor_cx.entity(),
14719            editors.last().expect("Asserted len is 1").clone()
14720        );
14721    });
14722
14723    set_up_lsp_handlers(true, &mut cx);
14724    let navigated = cx
14725        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14726        .await
14727        .expect("Failed to navigate to lookup references");
14728    assert_eq!(
14729        navigated,
14730        Navigated::Yes,
14731        "Should have navigated to references as a fallback after empty GoToDefinition response"
14732    );
14733    // We should not change the selections in the existing file,
14734    // if opening another milti buffer with the references
14735    cx.assert_editor_state(
14736        &r#"fn one() {
14737            let mut a = two();
14738        }
14739
14740        fn «twoˇ»() {}"#
14741            .unindent(),
14742    );
14743    let editors = cx.update_workspace(|workspace, _, cx| {
14744        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14745    });
14746    cx.update_editor(|_, _, test_editor_cx| {
14747        assert_eq!(
14748            editors.len(),
14749            2,
14750            "After falling back to references search, we open a new editor with the results"
14751        );
14752        let references_fallback_text = editors
14753            .into_iter()
14754            .find(|new_editor| *new_editor != test_editor_cx.entity())
14755            .expect("Should have one non-test editor now")
14756            .read(test_editor_cx)
14757            .text(test_editor_cx);
14758        assert_eq!(
14759            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14760            "Should use the range from the references response and not the GoToDefinition one"
14761        );
14762    });
14763}
14764
14765#[gpui::test]
14766async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14767    init_test(cx, |_| {});
14768
14769    let language = Arc::new(Language::new(
14770        LanguageConfig::default(),
14771        Some(tree_sitter_rust::LANGUAGE.into()),
14772    ));
14773
14774    let text = r#"
14775        #[cfg(test)]
14776        mod tests() {
14777            #[test]
14778            fn runnable_1() {
14779                let a = 1;
14780            }
14781
14782            #[test]
14783            fn runnable_2() {
14784                let a = 1;
14785                let b = 2;
14786            }
14787        }
14788    "#
14789    .unindent();
14790
14791    let fs = FakeFs::new(cx.executor());
14792    fs.insert_file("/file.rs", Default::default()).await;
14793
14794    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14795    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14796    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14797    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14798    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14799
14800    let editor = cx.new_window_entity(|window, cx| {
14801        Editor::new(
14802            EditorMode::Full,
14803            multi_buffer,
14804            Some(project.clone()),
14805            true,
14806            window,
14807            cx,
14808        )
14809    });
14810
14811    editor.update_in(cx, |editor, window, cx| {
14812        editor.tasks.insert(
14813            (buffer.read(cx).remote_id(), 3),
14814            RunnableTasks {
14815                templates: vec![],
14816                offset: MultiBufferOffset(43),
14817                column: 0,
14818                extra_variables: HashMap::default(),
14819                context_range: BufferOffset(43)..BufferOffset(85),
14820            },
14821        );
14822        editor.tasks.insert(
14823            (buffer.read(cx).remote_id(), 8),
14824            RunnableTasks {
14825                templates: vec![],
14826                offset: MultiBufferOffset(86),
14827                column: 0,
14828                extra_variables: HashMap::default(),
14829                context_range: BufferOffset(86)..BufferOffset(191),
14830            },
14831        );
14832
14833        // Test finding task when cursor is inside function body
14834        editor.change_selections(None, window, cx, |s| {
14835            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14836        });
14837        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14838        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14839
14840        // Test finding task when cursor is on function name
14841        editor.change_selections(None, window, cx, |s| {
14842            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14843        });
14844        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14845        assert_eq!(row, 8, "Should find task when cursor is on function name");
14846    });
14847}
14848
14849#[gpui::test]
14850async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14851    init_test(cx, |_| {});
14852
14853    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14854    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14855    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14856
14857    let fs = FakeFs::new(cx.executor());
14858    fs.insert_tree(
14859        "/a",
14860        json!({
14861            "first.rs": sample_text_1,
14862            "second.rs": sample_text_2,
14863            "third.rs": sample_text_3,
14864        }),
14865    )
14866    .await;
14867    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14868    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14869    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14870    let worktree = project.update(cx, |project, cx| {
14871        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14872        assert_eq!(worktrees.len(), 1);
14873        worktrees.pop().unwrap()
14874    });
14875    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14876
14877    let buffer_1 = project
14878        .update(cx, |project, cx| {
14879            project.open_buffer((worktree_id, "first.rs"), cx)
14880        })
14881        .await
14882        .unwrap();
14883    let buffer_2 = project
14884        .update(cx, |project, cx| {
14885            project.open_buffer((worktree_id, "second.rs"), cx)
14886        })
14887        .await
14888        .unwrap();
14889    let buffer_3 = project
14890        .update(cx, |project, cx| {
14891            project.open_buffer((worktree_id, "third.rs"), cx)
14892        })
14893        .await
14894        .unwrap();
14895
14896    let multi_buffer = cx.new(|cx| {
14897        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14898        multi_buffer.push_excerpts(
14899            buffer_1.clone(),
14900            [
14901                ExcerptRange {
14902                    context: Point::new(0, 0)..Point::new(3, 0),
14903                    primary: None,
14904                },
14905                ExcerptRange {
14906                    context: Point::new(5, 0)..Point::new(7, 0),
14907                    primary: None,
14908                },
14909                ExcerptRange {
14910                    context: Point::new(9, 0)..Point::new(10, 4),
14911                    primary: None,
14912                },
14913            ],
14914            cx,
14915        );
14916        multi_buffer.push_excerpts(
14917            buffer_2.clone(),
14918            [
14919                ExcerptRange {
14920                    context: Point::new(0, 0)..Point::new(3, 0),
14921                    primary: None,
14922                },
14923                ExcerptRange {
14924                    context: Point::new(5, 0)..Point::new(7, 0),
14925                    primary: None,
14926                },
14927                ExcerptRange {
14928                    context: Point::new(9, 0)..Point::new(10, 4),
14929                    primary: None,
14930                },
14931            ],
14932            cx,
14933        );
14934        multi_buffer.push_excerpts(
14935            buffer_3.clone(),
14936            [
14937                ExcerptRange {
14938                    context: Point::new(0, 0)..Point::new(3, 0),
14939                    primary: None,
14940                },
14941                ExcerptRange {
14942                    context: Point::new(5, 0)..Point::new(7, 0),
14943                    primary: None,
14944                },
14945                ExcerptRange {
14946                    context: Point::new(9, 0)..Point::new(10, 4),
14947                    primary: None,
14948                },
14949            ],
14950            cx,
14951        );
14952        multi_buffer
14953    });
14954    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14955        Editor::new(
14956            EditorMode::Full,
14957            multi_buffer,
14958            Some(project.clone()),
14959            true,
14960            window,
14961            cx,
14962        )
14963    });
14964
14965    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";
14966    assert_eq!(
14967        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14968        full_text,
14969    );
14970
14971    multi_buffer_editor.update(cx, |editor, cx| {
14972        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14973    });
14974    assert_eq!(
14975        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14976        "\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",
14977        "After folding the first buffer, its text should not be displayed"
14978    );
14979
14980    multi_buffer_editor.update(cx, |editor, cx| {
14981        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14982    });
14983    assert_eq!(
14984        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14985        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14986        "After folding the second buffer, its text should not be displayed"
14987    );
14988
14989    multi_buffer_editor.update(cx, |editor, cx| {
14990        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14991    });
14992    assert_eq!(
14993        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14994        "\n\n\n\n\n",
14995        "After folding the third buffer, its text should not be displayed"
14996    );
14997
14998    // Emulate selection inside the fold logic, that should work
14999    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15000        editor
15001            .snapshot(window, cx)
15002            .next_line_boundary(Point::new(0, 4));
15003    });
15004
15005    multi_buffer_editor.update(cx, |editor, cx| {
15006        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15007    });
15008    assert_eq!(
15009        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15010        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15011        "After unfolding the second buffer, its text should be displayed"
15012    );
15013
15014    multi_buffer_editor.update(cx, |editor, cx| {
15015        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15016    });
15017    assert_eq!(
15018        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15019        "\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",
15020        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15021    );
15022
15023    multi_buffer_editor.update(cx, |editor, cx| {
15024        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15025    });
15026    assert_eq!(
15027        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15028        full_text,
15029        "After unfolding the all buffers, all original text should be displayed"
15030    );
15031}
15032
15033#[gpui::test]
15034async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15035    init_test(cx, |_| {});
15036
15037    let sample_text_1 = "1111\n2222\n3333".to_string();
15038    let sample_text_2 = "4444\n5555\n6666".to_string();
15039    let sample_text_3 = "7777\n8888\n9999".to_string();
15040
15041    let fs = FakeFs::new(cx.executor());
15042    fs.insert_tree(
15043        "/a",
15044        json!({
15045            "first.rs": sample_text_1,
15046            "second.rs": sample_text_2,
15047            "third.rs": sample_text_3,
15048        }),
15049    )
15050    .await;
15051    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15052    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15053    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15054    let worktree = project.update(cx, |project, cx| {
15055        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15056        assert_eq!(worktrees.len(), 1);
15057        worktrees.pop().unwrap()
15058    });
15059    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15060
15061    let buffer_1 = project
15062        .update(cx, |project, cx| {
15063            project.open_buffer((worktree_id, "first.rs"), cx)
15064        })
15065        .await
15066        .unwrap();
15067    let buffer_2 = project
15068        .update(cx, |project, cx| {
15069            project.open_buffer((worktree_id, "second.rs"), cx)
15070        })
15071        .await
15072        .unwrap();
15073    let buffer_3 = project
15074        .update(cx, |project, cx| {
15075            project.open_buffer((worktree_id, "third.rs"), cx)
15076        })
15077        .await
15078        .unwrap();
15079
15080    let multi_buffer = cx.new(|cx| {
15081        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15082        multi_buffer.push_excerpts(
15083            buffer_1.clone(),
15084            [ExcerptRange {
15085                context: Point::new(0, 0)..Point::new(3, 0),
15086                primary: None,
15087            }],
15088            cx,
15089        );
15090        multi_buffer.push_excerpts(
15091            buffer_2.clone(),
15092            [ExcerptRange {
15093                context: Point::new(0, 0)..Point::new(3, 0),
15094                primary: None,
15095            }],
15096            cx,
15097        );
15098        multi_buffer.push_excerpts(
15099            buffer_3.clone(),
15100            [ExcerptRange {
15101                context: Point::new(0, 0)..Point::new(3, 0),
15102                primary: None,
15103            }],
15104            cx,
15105        );
15106        multi_buffer
15107    });
15108
15109    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15110        Editor::new(
15111            EditorMode::Full,
15112            multi_buffer,
15113            Some(project.clone()),
15114            true,
15115            window,
15116            cx,
15117        )
15118    });
15119
15120    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15121    assert_eq!(
15122        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15123        full_text,
15124    );
15125
15126    multi_buffer_editor.update(cx, |editor, cx| {
15127        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15128    });
15129    assert_eq!(
15130        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15131        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15132        "After folding the first buffer, its text should not be displayed"
15133    );
15134
15135    multi_buffer_editor.update(cx, |editor, cx| {
15136        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15137    });
15138
15139    assert_eq!(
15140        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15141        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15142        "After folding the second buffer, its text should not be displayed"
15143    );
15144
15145    multi_buffer_editor.update(cx, |editor, cx| {
15146        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15147    });
15148    assert_eq!(
15149        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15150        "\n\n\n\n\n",
15151        "After folding the third buffer, its text should not be displayed"
15152    );
15153
15154    multi_buffer_editor.update(cx, |editor, cx| {
15155        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15156    });
15157    assert_eq!(
15158        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15159        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15160        "After unfolding the second buffer, its text should be displayed"
15161    );
15162
15163    multi_buffer_editor.update(cx, |editor, cx| {
15164        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15165    });
15166    assert_eq!(
15167        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15168        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15169        "After unfolding the first buffer, its text should be displayed"
15170    );
15171
15172    multi_buffer_editor.update(cx, |editor, cx| {
15173        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15174    });
15175    assert_eq!(
15176        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15177        full_text,
15178        "After unfolding all buffers, all original text should be displayed"
15179    );
15180}
15181
15182#[gpui::test]
15183async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15184    init_test(cx, |_| {});
15185
15186    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15187
15188    let fs = FakeFs::new(cx.executor());
15189    fs.insert_tree(
15190        "/a",
15191        json!({
15192            "main.rs": sample_text,
15193        }),
15194    )
15195    .await;
15196    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15197    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15198    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15199    let worktree = project.update(cx, |project, cx| {
15200        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15201        assert_eq!(worktrees.len(), 1);
15202        worktrees.pop().unwrap()
15203    });
15204    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15205
15206    let buffer_1 = project
15207        .update(cx, |project, cx| {
15208            project.open_buffer((worktree_id, "main.rs"), cx)
15209        })
15210        .await
15211        .unwrap();
15212
15213    let multi_buffer = cx.new(|cx| {
15214        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15215        multi_buffer.push_excerpts(
15216            buffer_1.clone(),
15217            [ExcerptRange {
15218                context: Point::new(0, 0)
15219                    ..Point::new(
15220                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15221                        0,
15222                    ),
15223                primary: None,
15224            }],
15225            cx,
15226        );
15227        multi_buffer
15228    });
15229    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15230        Editor::new(
15231            EditorMode::Full,
15232            multi_buffer,
15233            Some(project.clone()),
15234            true,
15235            window,
15236            cx,
15237        )
15238    });
15239
15240    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15241    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15242        enum TestHighlight {}
15243        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15244        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15245        editor.highlight_text::<TestHighlight>(
15246            vec![highlight_range.clone()],
15247            HighlightStyle::color(Hsla::green()),
15248            cx,
15249        );
15250        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15251    });
15252
15253    let full_text = format!("\n\n\n{sample_text}\n");
15254    assert_eq!(
15255        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15256        full_text,
15257    );
15258}
15259
15260#[gpui::test]
15261fn test_inline_completion_text(cx: &mut TestAppContext) {
15262    init_test(cx, |_| {});
15263
15264    // Simple insertion
15265    {
15266        let window = cx.add_window(|window, cx| {
15267            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
15268            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15269        });
15270        let cx = &mut VisualTestContext::from_window(*window, cx);
15271
15272        window
15273            .update(cx, |editor, window, cx| {
15274                let snapshot = editor.snapshot(window, cx);
15275                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
15276                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
15277                let edits = vec![(edit_range, " beautiful".to_string())];
15278
15279                let InlineCompletionText::Edit { text, highlights } =
15280                    inline_completion_edit_text(&snapshot, &edits, false, cx)
15281                else {
15282                    panic!("Failed to generate inline completion text");
15283                };
15284
15285                assert_eq!(text, "Hello, beautiful world!");
15286                assert_eq!(highlights.len(), 1);
15287                assert_eq!(highlights[0].0, 6..16);
15288                assert_eq!(
15289                    highlights[0].1.background_color,
15290                    Some(cx.theme().status().created_background)
15291                );
15292            })
15293            .unwrap();
15294    }
15295
15296    // Replacement
15297    {
15298        let window = cx.add_window(|window, cx| {
15299            let buffer = MultiBuffer::build_simple("This is a test.", cx);
15300            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15301        });
15302        let cx = &mut VisualTestContext::from_window(*window, cx);
15303
15304        window
15305            .update(cx, |editor, window, cx| {
15306                let snapshot = editor.snapshot(window, cx);
15307                let edits = vec![(
15308                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
15309                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
15310                    "That".to_string(),
15311                )];
15312
15313                let InlineCompletionText::Edit { text, highlights } =
15314                    inline_completion_edit_text(&snapshot, &edits, false, cx)
15315                else {
15316                    panic!("Failed to generate inline completion text");
15317                };
15318
15319                assert_eq!(text, "That is a test.");
15320                assert_eq!(highlights.len(), 1);
15321                assert_eq!(highlights[0].0, 0..4);
15322                assert_eq!(
15323                    highlights[0].1.background_color,
15324                    Some(cx.theme().status().created_background)
15325                );
15326            })
15327            .unwrap();
15328    }
15329
15330    // Multiple edits
15331    {
15332        let window = cx.add_window(|window, cx| {
15333            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
15334            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15335        });
15336        let cx = &mut VisualTestContext::from_window(*window, cx);
15337
15338        window
15339            .update(cx, |editor, window, cx| {
15340                let snapshot = editor.snapshot(window, cx);
15341                let edits = vec![
15342                    (
15343                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
15344                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
15345                        "Greetings".into(),
15346                    ),
15347                    (
15348                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
15349                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
15350                        " and universe".into(),
15351                    ),
15352                ];
15353
15354                let InlineCompletionText::Edit { text, highlights } =
15355                    inline_completion_edit_text(&snapshot, &edits, false, cx)
15356                else {
15357                    panic!("Failed to generate inline completion text");
15358                };
15359
15360                assert_eq!(text, "Greetings, world and universe!");
15361                assert_eq!(highlights.len(), 2);
15362                assert_eq!(highlights[0].0, 0..9);
15363                assert_eq!(highlights[1].0, 16..29);
15364                assert_eq!(
15365                    highlights[0].1.background_color,
15366                    Some(cx.theme().status().created_background)
15367                );
15368                assert_eq!(
15369                    highlights[1].1.background_color,
15370                    Some(cx.theme().status().created_background)
15371                );
15372            })
15373            .unwrap();
15374    }
15375
15376    // Multiple lines with edits
15377    {
15378        let window = cx.add_window(|window, cx| {
15379            let buffer =
15380                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
15381            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15382        });
15383        let cx = &mut VisualTestContext::from_window(*window, cx);
15384
15385        window
15386            .update(cx, |editor, window, cx| {
15387                let snapshot = editor.snapshot(window, cx);
15388                let edits = vec![
15389                    (
15390                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
15391                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
15392                        "modified".to_string(),
15393                    ),
15394                    (
15395                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
15396                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
15397                        "New third line".to_string(),
15398                    ),
15399                    (
15400                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
15401                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
15402                        " updated".to_string(),
15403                    ),
15404                ];
15405
15406                let InlineCompletionText::Edit { text, highlights } =
15407                    inline_completion_edit_text(&snapshot, &edits, false, cx)
15408                else {
15409                    panic!("Failed to generate inline completion text");
15410                };
15411
15412                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
15413                assert_eq!(highlights.len(), 3);
15414                assert_eq!(highlights[0].0, 7..15); // "modified"
15415                assert_eq!(highlights[1].0, 16..30); // "New third line"
15416                assert_eq!(highlights[2].0, 37..45); // " updated"
15417
15418                for highlight in &highlights {
15419                    assert_eq!(
15420                        highlight.1.background_color,
15421                        Some(cx.theme().status().created_background)
15422                    );
15423                }
15424            })
15425            .unwrap();
15426    }
15427}
15428
15429#[gpui::test]
15430fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15431    init_test(cx, |_| {});
15432
15433    // Deletion
15434    {
15435        let window = cx.add_window(|window, cx| {
15436            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
15437            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15438        });
15439        let cx = &mut VisualTestContext::from_window(*window, cx);
15440
15441        window
15442            .update(cx, |editor, window, cx| {
15443                let snapshot = editor.snapshot(window, cx);
15444                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
15445                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
15446                let edits = vec![(edit_range, "".to_string())];
15447
15448                let InlineCompletionText::Edit { text, highlights } =
15449                    inline_completion_edit_text(&snapshot, &edits, true, cx)
15450                else {
15451                    panic!("Failed to generate inline completion text");
15452                };
15453
15454                assert_eq!(text, "Hello, world!");
15455                assert_eq!(highlights.len(), 1);
15456                assert_eq!(highlights[0].0, 5..11);
15457                assert_eq!(
15458                    highlights[0].1.background_color,
15459                    Some(cx.theme().status().deleted_background)
15460                );
15461            })
15462            .unwrap();
15463    }
15464
15465    // Insertion
15466    {
15467        let window = cx.add_window(|window, cx| {
15468            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
15469            Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15470        });
15471        let cx = &mut VisualTestContext::from_window(*window, cx);
15472
15473        window
15474            .update(cx, |editor, window, cx| {
15475                let snapshot = editor.snapshot(window, cx);
15476                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
15477                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
15478                let edits = vec![(edit_range, " digital".to_string())];
15479
15480                let InlineCompletionText::Edit { text, highlights } =
15481                    inline_completion_edit_text(&snapshot, &edits, true, cx)
15482                else {
15483                    panic!("Failed to generate inline completion text");
15484                };
15485
15486                assert_eq!(text, "Hello, digital world!");
15487                assert_eq!(highlights.len(), 1);
15488                assert_eq!(highlights[0].0, 6..14);
15489                assert_eq!(
15490                    highlights[0].1.background_color,
15491                    Some(cx.theme().status().created_background)
15492                );
15493            })
15494            .unwrap();
15495    }
15496}
15497
15498#[gpui::test]
15499async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15500    init_test(cx, |_| {});
15501    let capabilities = lsp::ServerCapabilities {
15502        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15503            prepare_provider: Some(true),
15504            work_done_progress_options: Default::default(),
15505        })),
15506        ..Default::default()
15507    };
15508    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15509
15510    cx.set_state(indoc! {"
15511        struct Fˇoo {}
15512    "});
15513
15514    cx.update_editor(|editor, _, cx| {
15515        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15516        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15517        editor.highlight_background::<DocumentHighlightRead>(
15518            &[highlight_range],
15519            |c| c.editor_document_highlight_read_background,
15520            cx,
15521        );
15522    });
15523
15524    let mut prepare_rename_handler =
15525        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15526            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15527                start: lsp::Position {
15528                    line: 0,
15529                    character: 7,
15530                },
15531                end: lsp::Position {
15532                    line: 0,
15533                    character: 10,
15534                },
15535            })))
15536        });
15537    let prepare_rename_task = cx
15538        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15539        .expect("Prepare rename was not started");
15540    prepare_rename_handler.next().await.unwrap();
15541    prepare_rename_task.await.expect("Prepare rename failed");
15542
15543    let mut rename_handler =
15544        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15545            let edit = lsp::TextEdit {
15546                range: lsp::Range {
15547                    start: lsp::Position {
15548                        line: 0,
15549                        character: 7,
15550                    },
15551                    end: lsp::Position {
15552                        line: 0,
15553                        character: 10,
15554                    },
15555                },
15556                new_text: "FooRenamed".to_string(),
15557            };
15558            Ok(Some(lsp::WorkspaceEdit::new(
15559                // Specify the same edit twice
15560                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15561            )))
15562        });
15563    let rename_task = cx
15564        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15565        .expect("Confirm rename was not started");
15566    rename_handler.next().await.unwrap();
15567    rename_task.await.expect("Confirm rename failed");
15568    cx.run_until_parked();
15569
15570    // Despite two edits, only one is actually applied as those are identical
15571    cx.assert_editor_state(indoc! {"
15572        struct FooRenamedˇ {}
15573    "});
15574}
15575
15576#[gpui::test]
15577async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15578    init_test(cx, |_| {});
15579    // These capabilities indicate that the server does not support prepare rename.
15580    let capabilities = lsp::ServerCapabilities {
15581        rename_provider: Some(lsp::OneOf::Left(true)),
15582        ..Default::default()
15583    };
15584    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15585
15586    cx.set_state(indoc! {"
15587        struct Fˇoo {}
15588    "});
15589
15590    cx.update_editor(|editor, _window, cx| {
15591        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15592        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15593        editor.highlight_background::<DocumentHighlightRead>(
15594            &[highlight_range],
15595            |c| c.editor_document_highlight_read_background,
15596            cx,
15597        );
15598    });
15599
15600    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15601        .expect("Prepare rename was not started")
15602        .await
15603        .expect("Prepare rename failed");
15604
15605    let mut rename_handler =
15606        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15607            let edit = lsp::TextEdit {
15608                range: lsp::Range {
15609                    start: lsp::Position {
15610                        line: 0,
15611                        character: 7,
15612                    },
15613                    end: lsp::Position {
15614                        line: 0,
15615                        character: 10,
15616                    },
15617                },
15618                new_text: "FooRenamed".to_string(),
15619            };
15620            Ok(Some(lsp::WorkspaceEdit::new(
15621                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15622            )))
15623        });
15624    let rename_task = cx
15625        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15626        .expect("Confirm rename was not started");
15627    rename_handler.next().await.unwrap();
15628    rename_task.await.expect("Confirm rename failed");
15629    cx.run_until_parked();
15630
15631    // Correct range is renamed, as `surrounding_word` is used to find it.
15632    cx.assert_editor_state(indoc! {"
15633        struct FooRenamedˇ {}
15634    "});
15635}
15636
15637fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15638    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15639    point..point
15640}
15641
15642fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15643    let (text, ranges) = marked_text_ranges(marked_text, true);
15644    assert_eq!(editor.text(cx), text);
15645    assert_eq!(
15646        editor.selections.ranges(cx),
15647        ranges,
15648        "Assert selections are {}",
15649        marked_text
15650    );
15651}
15652
15653pub fn handle_signature_help_request(
15654    cx: &mut EditorLspTestContext,
15655    mocked_response: lsp::SignatureHelp,
15656) -> impl Future<Output = ()> {
15657    let mut request =
15658        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15659            let mocked_response = mocked_response.clone();
15660            async move { Ok(Some(mocked_response)) }
15661        });
15662
15663    async move {
15664        request.next().await;
15665    }
15666}
15667
15668/// Handle completion request passing a marked string specifying where the completion
15669/// should be triggered from using '|' character, what range should be replaced, and what completions
15670/// should be returned using '<' and '>' to delimit the range
15671pub fn handle_completion_request(
15672    cx: &mut EditorLspTestContext,
15673    marked_string: &str,
15674    completions: Vec<&'static str>,
15675    counter: Arc<AtomicUsize>,
15676) -> impl Future<Output = ()> {
15677    let complete_from_marker: TextRangeMarker = '|'.into();
15678    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15679    let (_, mut marked_ranges) = marked_text_ranges_by(
15680        marked_string,
15681        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15682    );
15683
15684    let complete_from_position =
15685        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15686    let replace_range =
15687        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15688
15689    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15690        let completions = completions.clone();
15691        counter.fetch_add(1, atomic::Ordering::Release);
15692        async move {
15693            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15694            assert_eq!(
15695                params.text_document_position.position,
15696                complete_from_position
15697            );
15698            Ok(Some(lsp::CompletionResponse::Array(
15699                completions
15700                    .iter()
15701                    .map(|completion_text| lsp::CompletionItem {
15702                        label: completion_text.to_string(),
15703                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15704                            range: replace_range,
15705                            new_text: completion_text.to_string(),
15706                        })),
15707                        ..Default::default()
15708                    })
15709                    .collect(),
15710            )))
15711        }
15712    });
15713
15714    async move {
15715        request.next().await;
15716    }
15717}
15718
15719fn handle_resolve_completion_request(
15720    cx: &mut EditorLspTestContext,
15721    edits: Option<Vec<(&'static str, &'static str)>>,
15722) -> impl Future<Output = ()> {
15723    let edits = edits.map(|edits| {
15724        edits
15725            .iter()
15726            .map(|(marked_string, new_text)| {
15727                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15728                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15729                lsp::TextEdit::new(replace_range, new_text.to_string())
15730            })
15731            .collect::<Vec<_>>()
15732    });
15733
15734    let mut request =
15735        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15736            let edits = edits.clone();
15737            async move {
15738                Ok(lsp::CompletionItem {
15739                    additional_text_edits: edits,
15740                    ..Default::default()
15741                })
15742            }
15743        });
15744
15745    async move {
15746        request.next().await;
15747    }
15748}
15749
15750pub(crate) fn update_test_language_settings(
15751    cx: &mut TestAppContext,
15752    f: impl Fn(&mut AllLanguageSettingsContent),
15753) {
15754    cx.update(|cx| {
15755        SettingsStore::update_global(cx, |store, cx| {
15756            store.update_user_settings::<AllLanguageSettings>(cx, f);
15757        });
15758    });
15759}
15760
15761pub(crate) fn update_test_project_settings(
15762    cx: &mut TestAppContext,
15763    f: impl Fn(&mut ProjectSettings),
15764) {
15765    cx.update(|cx| {
15766        SettingsStore::update_global(cx, |store, cx| {
15767            store.update_user_settings::<ProjectSettings>(cx, f);
15768        });
15769    });
15770}
15771
15772pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15773    cx.update(|cx| {
15774        assets::Assets.load_test_fonts(cx);
15775        let store = SettingsStore::test(cx);
15776        cx.set_global(store);
15777        theme::init(theme::LoadThemes::JustBase, cx);
15778        release_channel::init(SemanticVersion::default(), cx);
15779        client::init_settings(cx);
15780        language::init(cx);
15781        Project::init_settings(cx);
15782        workspace::init_settings(cx);
15783        crate::init(cx);
15784    });
15785
15786    update_test_language_settings(cx, f);
15787}
15788
15789#[track_caller]
15790fn assert_hunk_revert(
15791    not_reverted_text_with_selections: &str,
15792    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15793    expected_reverted_text_with_selections: &str,
15794    base_text: &str,
15795    cx: &mut EditorLspTestContext,
15796) {
15797    cx.set_state(not_reverted_text_with_selections);
15798    cx.set_diff_base(base_text);
15799    cx.executor().run_until_parked();
15800
15801    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15802        let snapshot = editor.snapshot(window, cx);
15803        let reverted_hunk_statuses = snapshot
15804            .buffer_snapshot
15805            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15806            .map(|hunk| hunk.status())
15807            .collect::<Vec<_>>();
15808
15809        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15810        reverted_hunk_statuses
15811    });
15812    cx.executor().run_until_parked();
15813    cx.assert_editor_state(expected_reverted_text_with_selections);
15814    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15815}