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 buffer_diff::{BufferDiff, DiffHunkStatus, DiffHunkStatusKind};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::{IndentGuide, PathKey};
   28use parking_lot::Mutex;
   29use pretty_assertions::{assert_eq, assert_ne};
   30use project::project_settings::{LspSettings, ProjectSettings};
   31use project::FakeFs;
   32use serde_json::{self, json};
   33use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   34use std::{
   35    iter,
   36    sync::atomic::{self, AtomicUsize},
   37};
   38use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   39use text::ToPoint as _;
   40use unindent::Unindent;
   41use util::{
   42    assert_set_eq, path,
   43    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   44    uri,
   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 entity = cx.entity().clone();
   66            cx.subscribe_in(
   67                &entity,
   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(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(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(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        stop_at_indent: true,
 1515    };
 1516
 1517    let move_to_end = MoveToEndOfLine {
 1518        stop_at_soft_wraps: true,
 1519    };
 1520
 1521    let editor = cx.add_window(|window, cx| {
 1522        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1523        build_editor(buffer, window, cx)
 1524    });
 1525    _ = editor.update(cx, |editor, window, cx| {
 1526        editor.change_selections(None, window, cx, |s| {
 1527            s.select_display_ranges([
 1528                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1529                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1530            ]);
 1531        });
 1532    });
 1533
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1536        assert_eq!(
 1537            editor.selections.display_ranges(cx),
 1538            &[
 1539                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1540                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1541            ]
 1542        );
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_end_of_line(&move_to_end, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1573                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1574            ]
 1575        );
 1576    });
 1577
 1578    // Moving to the end of line again is a no-op.
 1579    _ = editor.update(cx, |editor, window, cx| {
 1580        editor.move_to_end_of_line(&move_to_end, window, cx);
 1581        assert_eq!(
 1582            editor.selections.display_ranges(cx),
 1583            &[
 1584                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1585                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1586            ]
 1587        );
 1588    });
 1589
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_left(&MoveLeft, window, cx);
 1592        editor.select_to_beginning_of_line(
 1593            &SelectToBeginningOfLine {
 1594                stop_at_soft_wraps: true,
 1595                stop_at_indent: true,
 1596            },
 1597            window,
 1598            cx,
 1599        );
 1600        assert_eq!(
 1601            editor.selections.display_ranges(cx),
 1602            &[
 1603                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1604                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1605            ]
 1606        );
 1607    });
 1608
 1609    _ = editor.update(cx, |editor, window, cx| {
 1610        editor.select_to_beginning_of_line(
 1611            &SelectToBeginningOfLine {
 1612                stop_at_soft_wraps: true,
 1613                stop_at_indent: true,
 1614            },
 1615            window,
 1616            cx,
 1617        );
 1618        assert_eq!(
 1619            editor.selections.display_ranges(cx),
 1620            &[
 1621                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1622                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1623            ]
 1624        );
 1625    });
 1626
 1627    _ = editor.update(cx, |editor, window, cx| {
 1628        editor.select_to_beginning_of_line(
 1629            &SelectToBeginningOfLine {
 1630                stop_at_soft_wraps: true,
 1631                stop_at_indent: true,
 1632            },
 1633            window,
 1634            cx,
 1635        );
 1636        assert_eq!(
 1637            editor.selections.display_ranges(cx),
 1638            &[
 1639                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1640                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1641            ]
 1642        );
 1643    });
 1644
 1645    _ = editor.update(cx, |editor, window, cx| {
 1646        editor.select_to_end_of_line(
 1647            &SelectToEndOfLine {
 1648                stop_at_soft_wraps: true,
 1649            },
 1650            window,
 1651            cx,
 1652        );
 1653        assert_eq!(
 1654            editor.selections.display_ranges(cx),
 1655            &[
 1656                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1657                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1658            ]
 1659        );
 1660    });
 1661
 1662    _ = editor.update(cx, |editor, window, cx| {
 1663        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1664        assert_eq!(editor.display_text(cx), "ab\n  de");
 1665        assert_eq!(
 1666            editor.selections.display_ranges(cx),
 1667            &[
 1668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1669                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1670            ]
 1671        );
 1672    });
 1673
 1674    _ = editor.update(cx, |editor, window, cx| {
 1675        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1676        assert_eq!(editor.display_text(cx), "\n");
 1677        assert_eq!(
 1678            editor.selections.display_ranges(cx),
 1679            &[
 1680                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1681                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1682            ]
 1683        );
 1684    });
 1685}
 1686
 1687#[gpui::test]
 1688fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1689    init_test(cx, |_| {});
 1690    let move_to_beg = MoveToBeginningOfLine {
 1691        stop_at_soft_wraps: false,
 1692        stop_at_indent: false,
 1693    };
 1694
 1695    let move_to_end = MoveToEndOfLine {
 1696        stop_at_soft_wraps: false,
 1697    };
 1698
 1699    let editor = cx.add_window(|window, cx| {
 1700        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1701        build_editor(buffer, window, cx)
 1702    });
 1703
 1704    _ = editor.update(cx, |editor, window, cx| {
 1705        editor.set_wrap_width(Some(140.0.into()), cx);
 1706
 1707        // We expect the following lines after wrapping
 1708        // ```
 1709        // thequickbrownfox
 1710        // jumpedoverthelazydo
 1711        // gs
 1712        // ```
 1713        // The final `gs` was soft-wrapped onto a new line.
 1714        assert_eq!(
 1715            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1716            editor.display_text(cx),
 1717        );
 1718
 1719        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1720        // Start the cursor at the `k` on the first line
 1721        editor.change_selections(None, window, cx, |s| {
 1722            s.select_display_ranges([
 1723                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1724            ]);
 1725        });
 1726
 1727        // Moving to the beginning of the line should put us at the beginning of the line.
 1728        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1729        assert_eq!(
 1730            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1731            editor.selections.display_ranges(cx)
 1732        );
 1733
 1734        // Moving to the end of the line should put us at the end of the line.
 1735        editor.move_to_end_of_line(&move_to_end, window, cx);
 1736        assert_eq!(
 1737            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1738            editor.selections.display_ranges(cx)
 1739        );
 1740
 1741        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1742        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1743        editor.change_selections(None, window, cx, |s| {
 1744            s.select_display_ranges([
 1745                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1746            ]);
 1747        });
 1748
 1749        // Moving to the beginning of the line should put us at the start of the second line of
 1750        // display text, i.e., the `j`.
 1751        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1752        assert_eq!(
 1753            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1754            editor.selections.display_ranges(cx)
 1755        );
 1756
 1757        // Moving to the beginning of the line again should be a no-op.
 1758        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1759        assert_eq!(
 1760            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1761            editor.selections.display_ranges(cx)
 1762        );
 1763
 1764        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1765        // next display line.
 1766        editor.move_to_end_of_line(&move_to_end, window, cx);
 1767        assert_eq!(
 1768            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1769            editor.selections.display_ranges(cx)
 1770        );
 1771
 1772        // Moving to the end of the line again should be a no-op.
 1773        editor.move_to_end_of_line(&move_to_end, window, cx);
 1774        assert_eq!(
 1775            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1776            editor.selections.display_ranges(cx)
 1777        );
 1778    });
 1779}
 1780
 1781#[gpui::test]
 1782fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1783    init_test(cx, |_| {});
 1784
 1785    let editor = cx.add_window(|window, cx| {
 1786        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1787        build_editor(buffer, window, cx)
 1788    });
 1789    _ = editor.update(cx, |editor, window, cx| {
 1790        editor.change_selections(None, window, cx, |s| {
 1791            s.select_display_ranges([
 1792                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1793                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1794            ])
 1795        });
 1796
 1797        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1798        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1799
 1800        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1801        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1802
 1803        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1804        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1805
 1806        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1807        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1808
 1809        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1810        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1811
 1812        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1813        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1814
 1815        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1816        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1817
 1818        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1819        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1820
 1821        editor.move_right(&MoveRight, window, cx);
 1822        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1823        assert_selection_ranges(
 1824            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1825            editor,
 1826            cx,
 1827        );
 1828
 1829        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1830        assert_selection_ranges(
 1831            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1832            editor,
 1833            cx,
 1834        );
 1835
 1836        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1837        assert_selection_ranges(
 1838            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1839            editor,
 1840            cx,
 1841        );
 1842    });
 1843}
 1844
 1845#[gpui::test]
 1846fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1847    init_test(cx, |_| {});
 1848
 1849    let editor = cx.add_window(|window, cx| {
 1850        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1851        build_editor(buffer, window, cx)
 1852    });
 1853
 1854    _ = editor.update(cx, |editor, window, cx| {
 1855        editor.set_wrap_width(Some(140.0.into()), cx);
 1856        assert_eq!(
 1857            editor.display_text(cx),
 1858            "use one::{\n    two::three::\n    four::five\n};"
 1859        );
 1860
 1861        editor.change_selections(None, window, cx, |s| {
 1862            s.select_display_ranges([
 1863                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1864            ]);
 1865        });
 1866
 1867        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1868        assert_eq!(
 1869            editor.selections.display_ranges(cx),
 1870            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1871        );
 1872
 1873        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1877        );
 1878
 1879        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1880        assert_eq!(
 1881            editor.selections.display_ranges(cx),
 1882            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1883        );
 1884
 1885        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1886        assert_eq!(
 1887            editor.selections.display_ranges(cx),
 1888            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1889        );
 1890
 1891        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1892        assert_eq!(
 1893            editor.selections.display_ranges(cx),
 1894            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1895        );
 1896
 1897        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1898        assert_eq!(
 1899            editor.selections.display_ranges(cx),
 1900            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1901        );
 1902    });
 1903}
 1904
 1905#[gpui::test]
 1906async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 1907    init_test(cx, |_| {});
 1908    let mut cx = EditorTestContext::new(cx).await;
 1909
 1910    let line_height = cx.editor(|editor, window, _| {
 1911        editor
 1912            .style()
 1913            .unwrap()
 1914            .text
 1915            .line_height_in_pixels(window.rem_size())
 1916    });
 1917    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1918
 1919    cx.set_state(
 1920        &r#"ˇone
 1921        two
 1922
 1923        three
 1924        fourˇ
 1925        five
 1926
 1927        six"#
 1928            .unindent(),
 1929    );
 1930
 1931    cx.update_editor(|editor, window, cx| {
 1932        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1933    });
 1934    cx.assert_editor_state(
 1935        &r#"one
 1936        two
 1937        ˇ
 1938        three
 1939        four
 1940        five
 1941        ˇ
 1942        six"#
 1943            .unindent(),
 1944    );
 1945
 1946    cx.update_editor(|editor, window, cx| {
 1947        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1948    });
 1949    cx.assert_editor_state(
 1950        &r#"one
 1951        two
 1952
 1953        three
 1954        four
 1955        five
 1956        ˇ
 1957        sixˇ"#
 1958            .unindent(),
 1959    );
 1960
 1961    cx.update_editor(|editor, window, cx| {
 1962        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1963    });
 1964    cx.assert_editor_state(
 1965        &r#"one
 1966        two
 1967
 1968        three
 1969        four
 1970        five
 1971
 1972        sixˇ"#
 1973            .unindent(),
 1974    );
 1975
 1976    cx.update_editor(|editor, window, cx| {
 1977        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1978    });
 1979    cx.assert_editor_state(
 1980        &r#"one
 1981        two
 1982
 1983        three
 1984        four
 1985        five
 1986        ˇ
 1987        six"#
 1988            .unindent(),
 1989    );
 1990
 1991    cx.update_editor(|editor, window, cx| {
 1992        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1993    });
 1994    cx.assert_editor_state(
 1995        &r#"one
 1996        two
 1997        ˇ
 1998        three
 1999        four
 2000        five
 2001
 2002        six"#
 2003            .unindent(),
 2004    );
 2005
 2006    cx.update_editor(|editor, window, cx| {
 2007        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2008    });
 2009    cx.assert_editor_state(
 2010        &r#"ˇone
 2011        two
 2012
 2013        three
 2014        four
 2015        five
 2016
 2017        six"#
 2018            .unindent(),
 2019    );
 2020}
 2021
 2022#[gpui::test]
 2023async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2024    init_test(cx, |_| {});
 2025    let mut cx = EditorTestContext::new(cx).await;
 2026    let line_height = cx.editor(|editor, window, _| {
 2027        editor
 2028            .style()
 2029            .unwrap()
 2030            .text
 2031            .line_height_in_pixels(window.rem_size())
 2032    });
 2033    let window = cx.window;
 2034    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2035
 2036    cx.set_state(
 2037        r#"ˇone
 2038        two
 2039        three
 2040        four
 2041        five
 2042        six
 2043        seven
 2044        eight
 2045        nine
 2046        ten
 2047        "#,
 2048    );
 2049
 2050    cx.update_editor(|editor, window, cx| {
 2051        assert_eq!(
 2052            editor.snapshot(window, cx).scroll_position(),
 2053            gpui::Point::new(0., 0.)
 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., 3.)
 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., 6.)
 2064        );
 2065        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2066        assert_eq!(
 2067            editor.snapshot(window, cx).scroll_position(),
 2068            gpui::Point::new(0., 3.)
 2069        );
 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., 1.)
 2075        );
 2076        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2077        assert_eq!(
 2078            editor.snapshot(window, cx).scroll_position(),
 2079            gpui::Point::new(0., 3.)
 2080        );
 2081    });
 2082}
 2083
 2084#[gpui::test]
 2085async fn test_autoscroll(cx: &mut TestAppContext) {
 2086    init_test(cx, |_| {});
 2087    let mut cx = EditorTestContext::new(cx).await;
 2088
 2089    let line_height = cx.update_editor(|editor, window, cx| {
 2090        editor.set_vertical_scroll_margin(2, cx);
 2091        editor
 2092            .style()
 2093            .unwrap()
 2094            .text
 2095            .line_height_in_pixels(window.rem_size())
 2096    });
 2097    let window = cx.window;
 2098    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2099
 2100    cx.set_state(
 2101        r#"ˇone
 2102            two
 2103            three
 2104            four
 2105            five
 2106            six
 2107            seven
 2108            eight
 2109            nine
 2110            ten
 2111        "#,
 2112    );
 2113    cx.update_editor(|editor, window, cx| {
 2114        assert_eq!(
 2115            editor.snapshot(window, cx).scroll_position(),
 2116            gpui::Point::new(0., 0.0)
 2117        );
 2118    });
 2119
 2120    // Add a cursor below the visible area. Since both cursors cannot fit
 2121    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2122    // allows the vertical scroll margin below that cursor.
 2123    cx.update_editor(|editor, window, cx| {
 2124        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2125            selections.select_ranges([
 2126                Point::new(0, 0)..Point::new(0, 0),
 2127                Point::new(6, 0)..Point::new(6, 0),
 2128            ]);
 2129        })
 2130    });
 2131    cx.update_editor(|editor, window, cx| {
 2132        assert_eq!(
 2133            editor.snapshot(window, cx).scroll_position(),
 2134            gpui::Point::new(0., 3.0)
 2135        );
 2136    });
 2137
 2138    // Move down. The editor cursor scrolls down to track the newest cursor.
 2139    cx.update_editor(|editor, window, cx| {
 2140        editor.move_down(&Default::default(), window, cx);
 2141    });
 2142    cx.update_editor(|editor, window, cx| {
 2143        assert_eq!(
 2144            editor.snapshot(window, cx).scroll_position(),
 2145            gpui::Point::new(0., 4.0)
 2146        );
 2147    });
 2148
 2149    // Add a cursor above the visible area. Since both cursors fit on screen,
 2150    // the editor scrolls to show both.
 2151    cx.update_editor(|editor, window, cx| {
 2152        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2153            selections.select_ranges([
 2154                Point::new(1, 0)..Point::new(1, 0),
 2155                Point::new(6, 0)..Point::new(6, 0),
 2156            ]);
 2157        })
 2158    });
 2159    cx.update_editor(|editor, window, cx| {
 2160        assert_eq!(
 2161            editor.snapshot(window, cx).scroll_position(),
 2162            gpui::Point::new(0., 1.0)
 2163        );
 2164    });
 2165}
 2166
 2167#[gpui::test]
 2168async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2169    init_test(cx, |_| {});
 2170    let mut cx = EditorTestContext::new(cx).await;
 2171
 2172    let line_height = cx.editor(|editor, window, _cx| {
 2173        editor
 2174            .style()
 2175            .unwrap()
 2176            .text
 2177            .line_height_in_pixels(window.rem_size())
 2178    });
 2179    let window = cx.window;
 2180    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2181    cx.set_state(
 2182        &r#"
 2183        ˇone
 2184        two
 2185        threeˇ
 2186        four
 2187        five
 2188        six
 2189        seven
 2190        eight
 2191        nine
 2192        ten
 2193        "#
 2194        .unindent(),
 2195    );
 2196
 2197    cx.update_editor(|editor, window, cx| {
 2198        editor.move_page_down(&MovePageDown::default(), window, cx)
 2199    });
 2200    cx.assert_editor_state(
 2201        &r#"
 2202        one
 2203        two
 2204        three
 2205        ˇfour
 2206        five
 2207        sixˇ
 2208        seven
 2209        eight
 2210        nine
 2211        ten
 2212        "#
 2213        .unindent(),
 2214    );
 2215
 2216    cx.update_editor(|editor, window, cx| {
 2217        editor.move_page_down(&MovePageDown::default(), window, cx)
 2218    });
 2219    cx.assert_editor_state(
 2220        &r#"
 2221        one
 2222        two
 2223        three
 2224        four
 2225        five
 2226        six
 2227        ˇseven
 2228        eight
 2229        nineˇ
 2230        ten
 2231        "#
 2232        .unindent(),
 2233    );
 2234
 2235    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2236    cx.assert_editor_state(
 2237        &r#"
 2238        one
 2239        two
 2240        three
 2241        ˇfour
 2242        five
 2243        sixˇ
 2244        seven
 2245        eight
 2246        nine
 2247        ten
 2248        "#
 2249        .unindent(),
 2250    );
 2251
 2252    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2253    cx.assert_editor_state(
 2254        &r#"
 2255        ˇone
 2256        two
 2257        threeˇ
 2258        four
 2259        five
 2260        six
 2261        seven
 2262        eight
 2263        nine
 2264        ten
 2265        "#
 2266        .unindent(),
 2267    );
 2268
 2269    // Test select collapsing
 2270    cx.update_editor(|editor, window, cx| {
 2271        editor.move_page_down(&MovePageDown::default(), window, cx);
 2272        editor.move_page_down(&MovePageDown::default(), window, cx);
 2273        editor.move_page_down(&MovePageDown::default(), window, cx);
 2274    });
 2275    cx.assert_editor_state(
 2276        &r#"
 2277        one
 2278        two
 2279        three
 2280        four
 2281        five
 2282        six
 2283        seven
 2284        eight
 2285        nine
 2286        ˇten
 2287        ˇ"#
 2288        .unindent(),
 2289    );
 2290}
 2291
 2292#[gpui::test]
 2293async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2294    init_test(cx, |_| {});
 2295    let mut cx = EditorTestContext::new(cx).await;
 2296    cx.set_state("one «two threeˇ» four");
 2297    cx.update_editor(|editor, window, cx| {
 2298        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2299        assert_eq!(editor.text(cx), " four");
 2300    });
 2301}
 2302
 2303#[gpui::test]
 2304fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2305    init_test(cx, |_| {});
 2306
 2307    let editor = cx.add_window(|window, cx| {
 2308        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2309        build_editor(buffer.clone(), window, cx)
 2310    });
 2311
 2312    _ = editor.update(cx, |editor, window, cx| {
 2313        editor.change_selections(None, window, cx, |s| {
 2314            s.select_display_ranges([
 2315                // an empty selection - the preceding word fragment is deleted
 2316                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2317                // characters selected - they are deleted
 2318                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2319            ])
 2320        });
 2321        editor.delete_to_previous_word_start(
 2322            &DeleteToPreviousWordStart {
 2323                ignore_newlines: false,
 2324            },
 2325            window,
 2326            cx,
 2327        );
 2328        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2329    });
 2330
 2331    _ = editor.update(cx, |editor, window, cx| {
 2332        editor.change_selections(None, window, cx, |s| {
 2333            s.select_display_ranges([
 2334                // an empty selection - the following word fragment is deleted
 2335                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2336                // characters selected - they are deleted
 2337                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2338            ])
 2339        });
 2340        editor.delete_to_next_word_end(
 2341            &DeleteToNextWordEnd {
 2342                ignore_newlines: false,
 2343            },
 2344            window,
 2345            cx,
 2346        );
 2347        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2348    });
 2349}
 2350
 2351#[gpui::test]
 2352fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2353    init_test(cx, |_| {});
 2354
 2355    let editor = cx.add_window(|window, cx| {
 2356        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2357        build_editor(buffer.clone(), window, cx)
 2358    });
 2359    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2360        ignore_newlines: false,
 2361    };
 2362    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2363        ignore_newlines: true,
 2364    };
 2365
 2366    _ = editor.update(cx, |editor, window, cx| {
 2367        editor.change_selections(None, window, cx, |s| {
 2368            s.select_display_ranges([
 2369                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2370            ])
 2371        });
 2372        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2373        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2374        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2375        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2376        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2377        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2378        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2379        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2380        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2381        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2382        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2383        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2384    });
 2385}
 2386
 2387#[gpui::test]
 2388fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2389    init_test(cx, |_| {});
 2390
 2391    let editor = cx.add_window(|window, cx| {
 2392        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2393        build_editor(buffer.clone(), window, cx)
 2394    });
 2395    let del_to_next_word_end = DeleteToNextWordEnd {
 2396        ignore_newlines: false,
 2397    };
 2398    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2399        ignore_newlines: true,
 2400    };
 2401
 2402    _ = editor.update(cx, |editor, window, cx| {
 2403        editor.change_selections(None, window, cx, |s| {
 2404            s.select_display_ranges([
 2405                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2406            ])
 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            "one\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            "\n   two\nthree\n   four"
 2417        );
 2418        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2419        assert_eq!(
 2420            editor.buffer.read(cx).read(cx).text(),
 2421            "two\nthree\n   four"
 2422        );
 2423        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2424        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2425        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2426        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2427        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2428        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2429    });
 2430}
 2431
 2432#[gpui::test]
 2433fn test_newline(cx: &mut TestAppContext) {
 2434    init_test(cx, |_| {});
 2435
 2436    let editor = cx.add_window(|window, cx| {
 2437        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2438        build_editor(buffer.clone(), window, cx)
 2439    });
 2440
 2441    _ = editor.update(cx, |editor, window, cx| {
 2442        editor.change_selections(None, window, cx, |s| {
 2443            s.select_display_ranges([
 2444                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2445                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2446                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2447            ])
 2448        });
 2449
 2450        editor.newline(&Newline, window, cx);
 2451        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2452    });
 2453}
 2454
 2455#[gpui::test]
 2456fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2457    init_test(cx, |_| {});
 2458
 2459    let editor = cx.add_window(|window, cx| {
 2460        let buffer = MultiBuffer::build_simple(
 2461            "
 2462                a
 2463                b(
 2464                    X
 2465                )
 2466                c(
 2467                    X
 2468                )
 2469            "
 2470            .unindent()
 2471            .as_str(),
 2472            cx,
 2473        );
 2474        let mut editor = build_editor(buffer.clone(), window, cx);
 2475        editor.change_selections(None, window, cx, |s| {
 2476            s.select_ranges([
 2477                Point::new(2, 4)..Point::new(2, 5),
 2478                Point::new(5, 4)..Point::new(5, 5),
 2479            ])
 2480        });
 2481        editor
 2482    });
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2486        editor.buffer.update(cx, |buffer, cx| {
 2487            buffer.edit(
 2488                [
 2489                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2490                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2491                ],
 2492                None,
 2493                cx,
 2494            );
 2495            assert_eq!(
 2496                buffer.read(cx).text(),
 2497                "
 2498                    a
 2499                    b()
 2500                    c()
 2501                "
 2502                .unindent()
 2503            );
 2504        });
 2505        assert_eq!(
 2506            editor.selections.ranges(cx),
 2507            &[
 2508                Point::new(1, 2)..Point::new(1, 2),
 2509                Point::new(2, 2)..Point::new(2, 2),
 2510            ],
 2511        );
 2512
 2513        editor.newline(&Newline, window, cx);
 2514        assert_eq!(
 2515            editor.text(cx),
 2516            "
 2517                a
 2518                b(
 2519                )
 2520                c(
 2521                )
 2522            "
 2523            .unindent()
 2524        );
 2525
 2526        // The selections are moved after the inserted newlines
 2527        assert_eq!(
 2528            editor.selections.ranges(cx),
 2529            &[
 2530                Point::new(2, 0)..Point::new(2, 0),
 2531                Point::new(4, 0)..Point::new(4, 0),
 2532            ],
 2533        );
 2534    });
 2535}
 2536
 2537#[gpui::test]
 2538async fn test_newline_above(cx: &mut TestAppContext) {
 2539    init_test(cx, |settings| {
 2540        settings.defaults.tab_size = NonZeroU32::new(4)
 2541    });
 2542
 2543    let language = Arc::new(
 2544        Language::new(
 2545            LanguageConfig::default(),
 2546            Some(tree_sitter_rust::LANGUAGE.into()),
 2547        )
 2548        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2549        .unwrap(),
 2550    );
 2551
 2552    let mut cx = EditorTestContext::new(cx).await;
 2553    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2554    cx.set_state(indoc! {"
 2555        const a: ˇA = (
 2556 2557                «const_functionˇ»(ˇ),
 2558                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2559 2560        ˇ);ˇ
 2561    "});
 2562
 2563    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2564    cx.assert_editor_state(indoc! {"
 2565        ˇ
 2566        const a: A = (
 2567            ˇ
 2568            (
 2569                ˇ
 2570                ˇ
 2571                const_function(),
 2572                ˇ
 2573                ˇ
 2574                ˇ
 2575                ˇ
 2576                something_else,
 2577                ˇ
 2578            )
 2579            ˇ
 2580            ˇ
 2581        );
 2582    "});
 2583}
 2584
 2585#[gpui::test]
 2586async fn test_newline_below(cx: &mut TestAppContext) {
 2587    init_test(cx, |settings| {
 2588        settings.defaults.tab_size = NonZeroU32::new(4)
 2589    });
 2590
 2591    let language = Arc::new(
 2592        Language::new(
 2593            LanguageConfig::default(),
 2594            Some(tree_sitter_rust::LANGUAGE.into()),
 2595        )
 2596        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2597        .unwrap(),
 2598    );
 2599
 2600    let mut cx = EditorTestContext::new(cx).await;
 2601    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2602    cx.set_state(indoc! {"
 2603        const a: ˇA = (
 2604 2605                «const_functionˇ»(ˇ),
 2606                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2607 2608        ˇ);ˇ
 2609    "});
 2610
 2611    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2612    cx.assert_editor_state(indoc! {"
 2613        const a: A = (
 2614            ˇ
 2615            (
 2616                ˇ
 2617                const_function(),
 2618                ˇ
 2619                ˇ
 2620                something_else,
 2621                ˇ
 2622                ˇ
 2623                ˇ
 2624                ˇ
 2625            )
 2626            ˇ
 2627        );
 2628        ˇ
 2629        ˇ
 2630    "});
 2631}
 2632
 2633#[gpui::test]
 2634async fn test_newline_comments(cx: &mut TestAppContext) {
 2635    init_test(cx, |settings| {
 2636        settings.defaults.tab_size = NonZeroU32::new(4)
 2637    });
 2638
 2639    let language = Arc::new(Language::new(
 2640        LanguageConfig {
 2641            line_comments: vec!["//".into()],
 2642            ..LanguageConfig::default()
 2643        },
 2644        None,
 2645    ));
 2646    {
 2647        let mut cx = EditorTestContext::new(cx).await;
 2648        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2649        cx.set_state(indoc! {"
 2650        // Fooˇ
 2651    "});
 2652
 2653        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2654        cx.assert_editor_state(indoc! {"
 2655        // Foo
 2656        //ˇ
 2657    "});
 2658        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2659        cx.set_state(indoc! {"
 2660        ˇ// Foo
 2661    "});
 2662        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2663        cx.assert_editor_state(indoc! {"
 2664
 2665        ˇ// Foo
 2666    "});
 2667    }
 2668    // Ensure that comment continuations can be disabled.
 2669    update_test_language_settings(cx, |settings| {
 2670        settings.defaults.extend_comment_on_newline = Some(false);
 2671    });
 2672    let mut cx = EditorTestContext::new(cx).await;
 2673    cx.set_state(indoc! {"
 2674        // Fooˇ
 2675    "});
 2676    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2677    cx.assert_editor_state(indoc! {"
 2678        // Foo
 2679        ˇ
 2680    "});
 2681}
 2682
 2683#[gpui::test]
 2684fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2685    init_test(cx, |_| {});
 2686
 2687    let editor = cx.add_window(|window, cx| {
 2688        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2689        let mut editor = build_editor(buffer.clone(), window, cx);
 2690        editor.change_selections(None, window, cx, |s| {
 2691            s.select_ranges([3..4, 11..12, 19..20])
 2692        });
 2693        editor
 2694    });
 2695
 2696    _ = editor.update(cx, |editor, window, cx| {
 2697        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2698        editor.buffer.update(cx, |buffer, cx| {
 2699            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2700            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2701        });
 2702        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2703
 2704        editor.insert("Z", window, cx);
 2705        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2706
 2707        // The selections are moved after the inserted characters
 2708        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2709    });
 2710}
 2711
 2712#[gpui::test]
 2713async fn test_tab(cx: &mut TestAppContext) {
 2714    init_test(cx, |settings| {
 2715        settings.defaults.tab_size = NonZeroU32::new(3)
 2716    });
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.set_state(indoc! {"
 2720        ˇabˇc
 2721        ˇ🏀ˇ🏀ˇefg
 2722 2723    "});
 2724    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2725    cx.assert_editor_state(indoc! {"
 2726           ˇab ˇc
 2727           ˇ🏀  ˇ🏀  ˇefg
 2728        d  ˇ
 2729    "});
 2730
 2731    cx.set_state(indoc! {"
 2732        a
 2733        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2734    "});
 2735    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2736    cx.assert_editor_state(indoc! {"
 2737        a
 2738           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2739    "});
 2740}
 2741
 2742#[gpui::test]
 2743async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2744    init_test(cx, |_| {});
 2745
 2746    let mut cx = EditorTestContext::new(cx).await;
 2747    let language = Arc::new(
 2748        Language::new(
 2749            LanguageConfig::default(),
 2750            Some(tree_sitter_rust::LANGUAGE.into()),
 2751        )
 2752        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2753        .unwrap(),
 2754    );
 2755    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2756
 2757    // cursors that are already at the suggested indent level insert
 2758    // a soft tab. cursors that are to the left of the suggested indent
 2759    // auto-indent their line.
 2760    cx.set_state(indoc! {"
 2761        ˇ
 2762        const a: B = (
 2763            c(
 2764                d(
 2765        ˇ
 2766                )
 2767        ˇ
 2768        ˇ    )
 2769        );
 2770    "});
 2771    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2772    cx.assert_editor_state(indoc! {"
 2773            ˇ
 2774        const a: B = (
 2775            c(
 2776                d(
 2777                    ˇ
 2778                )
 2779                ˇ
 2780            ˇ)
 2781        );
 2782    "});
 2783
 2784    // handle auto-indent when there are multiple cursors on the same line
 2785    cx.set_state(indoc! {"
 2786        const a: B = (
 2787            c(
 2788        ˇ    ˇ
 2789        ˇ    )
 2790        );
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        const a: B = (
 2795            c(
 2796                ˇ
 2797            ˇ)
 2798        );
 2799    "});
 2800}
 2801
 2802#[gpui::test]
 2803async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2804    init_test(cx, |settings| {
 2805        settings.defaults.tab_size = NonZeroU32::new(4)
 2806    });
 2807
 2808    let language = Arc::new(
 2809        Language::new(
 2810            LanguageConfig::default(),
 2811            Some(tree_sitter_rust::LANGUAGE.into()),
 2812        )
 2813        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2814        .unwrap(),
 2815    );
 2816
 2817    let mut cx = EditorTestContext::new(cx).await;
 2818    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2819    cx.set_state(indoc! {"
 2820        fn a() {
 2821            if b {
 2822        \t ˇc
 2823            }
 2824        }
 2825    "});
 2826
 2827    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2828    cx.assert_editor_state(indoc! {"
 2829        fn a() {
 2830            if b {
 2831                ˇc
 2832            }
 2833        }
 2834    "});
 2835}
 2836
 2837#[gpui::test]
 2838async fn test_indent_outdent(cx: &mut TestAppContext) {
 2839    init_test(cx, |settings| {
 2840        settings.defaults.tab_size = NonZeroU32::new(4);
 2841    });
 2842
 2843    let mut cx = EditorTestContext::new(cx).await;
 2844
 2845    cx.set_state(indoc! {"
 2846          «oneˇ» «twoˇ»
 2847        three
 2848         four
 2849    "});
 2850    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2851    cx.assert_editor_state(indoc! {"
 2852            «oneˇ» «twoˇ»
 2853        three
 2854         four
 2855    "});
 2856
 2857    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2858    cx.assert_editor_state(indoc! {"
 2859        «oneˇ» «twoˇ»
 2860        three
 2861         four
 2862    "});
 2863
 2864    // select across line ending
 2865    cx.set_state(indoc! {"
 2866        one two
 2867        t«hree
 2868        ˇ» four
 2869    "});
 2870    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2871    cx.assert_editor_state(indoc! {"
 2872        one two
 2873            t«hree
 2874        ˇ» four
 2875    "});
 2876
 2877    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2878    cx.assert_editor_state(indoc! {"
 2879        one two
 2880        t«hree
 2881        ˇ» four
 2882    "});
 2883
 2884    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2885    cx.set_state(indoc! {"
 2886        one two
 2887        ˇthree
 2888            four
 2889    "});
 2890    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2891    cx.assert_editor_state(indoc! {"
 2892        one two
 2893            ˇthree
 2894            four
 2895    "});
 2896
 2897    cx.set_state(indoc! {"
 2898        one two
 2899        ˇ    three
 2900            four
 2901    "});
 2902    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2903    cx.assert_editor_state(indoc! {"
 2904        one two
 2905        ˇthree
 2906            four
 2907    "});
 2908}
 2909
 2910#[gpui::test]
 2911async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 2912    init_test(cx, |settings| {
 2913        settings.defaults.hard_tabs = Some(true);
 2914    });
 2915
 2916    let mut cx = EditorTestContext::new(cx).await;
 2917
 2918    // select two ranges on one line
 2919    cx.set_state(indoc! {"
 2920        «oneˇ» «twoˇ»
 2921        three
 2922        four
 2923    "});
 2924    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        \t«oneˇ» «twoˇ»
 2927        three
 2928        four
 2929    "});
 2930    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2931    cx.assert_editor_state(indoc! {"
 2932        \t\t«oneˇ» «twoˇ»
 2933        three
 2934        four
 2935    "});
 2936    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2937    cx.assert_editor_state(indoc! {"
 2938        \t«oneˇ» «twoˇ»
 2939        three
 2940        four
 2941    "});
 2942    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2943    cx.assert_editor_state(indoc! {"
 2944        «oneˇ» «twoˇ»
 2945        three
 2946        four
 2947    "});
 2948
 2949    // select across a line ending
 2950    cx.set_state(indoc! {"
 2951        one two
 2952        t«hree
 2953        ˇ»four
 2954    "});
 2955    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        one two
 2958        \tt«hree
 2959        ˇ»four
 2960    "});
 2961    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2962    cx.assert_editor_state(indoc! {"
 2963        one two
 2964        \t\tt«hree
 2965        ˇ»four
 2966    "});
 2967    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2968    cx.assert_editor_state(indoc! {"
 2969        one two
 2970        \tt«hree
 2971        ˇ»four
 2972    "});
 2973    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2974    cx.assert_editor_state(indoc! {"
 2975        one two
 2976        t«hree
 2977        ˇ»four
 2978    "});
 2979
 2980    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2981    cx.set_state(indoc! {"
 2982        one two
 2983        ˇthree
 2984        four
 2985    "});
 2986    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2987    cx.assert_editor_state(indoc! {"
 2988        one two
 2989        ˇthree
 2990        four
 2991    "});
 2992    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        one two
 2995        \tˇthree
 2996        four
 2997    "});
 2998    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        one two
 3001        ˇthree
 3002        four
 3003    "});
 3004}
 3005
 3006#[gpui::test]
 3007fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3008    init_test(cx, |settings| {
 3009        settings.languages.extend([
 3010            (
 3011                "TOML".into(),
 3012                LanguageSettingsContent {
 3013                    tab_size: NonZeroU32::new(2),
 3014                    ..Default::default()
 3015                },
 3016            ),
 3017            (
 3018                "Rust".into(),
 3019                LanguageSettingsContent {
 3020                    tab_size: NonZeroU32::new(4),
 3021                    ..Default::default()
 3022                },
 3023            ),
 3024        ]);
 3025    });
 3026
 3027    let toml_language = Arc::new(Language::new(
 3028        LanguageConfig {
 3029            name: "TOML".into(),
 3030            ..Default::default()
 3031        },
 3032        None,
 3033    ));
 3034    let rust_language = Arc::new(Language::new(
 3035        LanguageConfig {
 3036            name: "Rust".into(),
 3037            ..Default::default()
 3038        },
 3039        None,
 3040    ));
 3041
 3042    let toml_buffer =
 3043        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3044    let rust_buffer =
 3045        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3046    let multibuffer = cx.new(|cx| {
 3047        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3048        multibuffer.push_excerpts(
 3049            toml_buffer.clone(),
 3050            [ExcerptRange {
 3051                context: Point::new(0, 0)..Point::new(2, 0),
 3052                primary: None,
 3053            }],
 3054            cx,
 3055        );
 3056        multibuffer.push_excerpts(
 3057            rust_buffer.clone(),
 3058            [ExcerptRange {
 3059                context: Point::new(0, 0)..Point::new(1, 0),
 3060                primary: None,
 3061            }],
 3062            cx,
 3063        );
 3064        multibuffer
 3065    });
 3066
 3067    cx.add_window(|window, cx| {
 3068        let mut editor = build_editor(multibuffer, window, cx);
 3069
 3070        assert_eq!(
 3071            editor.text(cx),
 3072            indoc! {"
 3073                a = 1
 3074                b = 2
 3075
 3076                const c: usize = 3;
 3077            "}
 3078        );
 3079
 3080        select_ranges(
 3081            &mut editor,
 3082            indoc! {"
 3083                «aˇ» = 1
 3084                b = 2
 3085
 3086                «const c:ˇ» usize = 3;
 3087            "},
 3088            window,
 3089            cx,
 3090        );
 3091
 3092        editor.tab(&Tab, window, cx);
 3093        assert_text_with_selections(
 3094            &mut editor,
 3095            indoc! {"
 3096                  «aˇ» = 1
 3097                b = 2
 3098
 3099                    «const c:ˇ» usize = 3;
 3100            "},
 3101            cx,
 3102        );
 3103        editor.tab_prev(&TabPrev, window, cx);
 3104        assert_text_with_selections(
 3105            &mut editor,
 3106            indoc! {"
 3107                «aˇ» = 1
 3108                b = 2
 3109
 3110                «const c:ˇ» usize = 3;
 3111            "},
 3112            cx,
 3113        );
 3114
 3115        editor
 3116    });
 3117}
 3118
 3119#[gpui::test]
 3120async fn test_backspace(cx: &mut TestAppContext) {
 3121    init_test(cx, |_| {});
 3122
 3123    let mut cx = EditorTestContext::new(cx).await;
 3124
 3125    // Basic backspace
 3126    cx.set_state(indoc! {"
 3127        onˇe two three
 3128        fou«rˇ» five six
 3129        seven «ˇeight nine
 3130        »ten
 3131    "});
 3132    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3133    cx.assert_editor_state(indoc! {"
 3134        oˇe two three
 3135        fouˇ five six
 3136        seven ˇten
 3137    "});
 3138
 3139    // Test backspace inside and around indents
 3140    cx.set_state(indoc! {"
 3141        zero
 3142            ˇone
 3143                ˇtwo
 3144            ˇ ˇ ˇ  three
 3145        ˇ  ˇ  four
 3146    "});
 3147    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3148    cx.assert_editor_state(indoc! {"
 3149        zero
 3150        ˇone
 3151            ˇtwo
 3152        ˇ  threeˇ  four
 3153    "});
 3154
 3155    // Test backspace with line_mode set to true
 3156    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3157    cx.set_state(indoc! {"
 3158        The ˇquick ˇbrown
 3159        fox jumps over
 3160        the lazy dog
 3161        ˇThe qu«ick bˇ»rown"});
 3162    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        ˇfox jumps over
 3165        the lazy dogˇ"});
 3166}
 3167
 3168#[gpui::test]
 3169async fn test_delete(cx: &mut TestAppContext) {
 3170    init_test(cx, |_| {});
 3171
 3172    let mut cx = EditorTestContext::new(cx).await;
 3173    cx.set_state(indoc! {"
 3174        onˇe two three
 3175        fou«rˇ» five six
 3176        seven «ˇeight nine
 3177        »ten
 3178    "});
 3179    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3180    cx.assert_editor_state(indoc! {"
 3181        onˇ two three
 3182        fouˇ five six
 3183        seven ˇten
 3184    "});
 3185
 3186    // Test backspace with line_mode set to true
 3187    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3188    cx.set_state(indoc! {"
 3189        The ˇquick ˇbrown
 3190        fox «ˇjum»ps over
 3191        the lazy dog
 3192        ˇThe qu«ick bˇ»rown"});
 3193    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3194    cx.assert_editor_state("ˇthe lazy dogˇ");
 3195}
 3196
 3197#[gpui::test]
 3198fn test_delete_line(cx: &mut TestAppContext) {
 3199    init_test(cx, |_| {});
 3200
 3201    let editor = cx.add_window(|window, cx| {
 3202        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3203        build_editor(buffer, window, cx)
 3204    });
 3205    _ = editor.update(cx, |editor, window, cx| {
 3206        editor.change_selections(None, window, cx, |s| {
 3207            s.select_display_ranges([
 3208                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3209                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3210                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3211            ])
 3212        });
 3213        editor.delete_line(&DeleteLine, window, cx);
 3214        assert_eq!(editor.display_text(cx), "ghi");
 3215        assert_eq!(
 3216            editor.selections.display_ranges(cx),
 3217            vec![
 3218                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3219                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3220            ]
 3221        );
 3222    });
 3223
 3224    let editor = cx.add_window(|window, cx| {
 3225        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3226        build_editor(buffer, window, cx)
 3227    });
 3228    _ = editor.update(cx, |editor, window, cx| {
 3229        editor.change_selections(None, window, cx, |s| {
 3230            s.select_display_ranges([
 3231                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3232            ])
 3233        });
 3234        editor.delete_line(&DeleteLine, window, cx);
 3235        assert_eq!(editor.display_text(cx), "ghi\n");
 3236        assert_eq!(
 3237            editor.selections.display_ranges(cx),
 3238            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3239        );
 3240    });
 3241}
 3242
 3243#[gpui::test]
 3244fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3245    init_test(cx, |_| {});
 3246
 3247    cx.add_window(|window, cx| {
 3248        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3249        let mut editor = build_editor(buffer.clone(), window, cx);
 3250        let buffer = buffer.read(cx).as_singleton().unwrap();
 3251
 3252        assert_eq!(
 3253            editor.selections.ranges::<Point>(cx),
 3254            &[Point::new(0, 0)..Point::new(0, 0)]
 3255        );
 3256
 3257        // When on single line, replace newline at end by space
 3258        editor.join_lines(&JoinLines, window, cx);
 3259        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3260        assert_eq!(
 3261            editor.selections.ranges::<Point>(cx),
 3262            &[Point::new(0, 3)..Point::new(0, 3)]
 3263        );
 3264
 3265        // When multiple lines are selected, remove newlines that are spanned by the selection
 3266        editor.change_selections(None, window, cx, |s| {
 3267            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3268        });
 3269        editor.join_lines(&JoinLines, window, cx);
 3270        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3271        assert_eq!(
 3272            editor.selections.ranges::<Point>(cx),
 3273            &[Point::new(0, 11)..Point::new(0, 11)]
 3274        );
 3275
 3276        // Undo should be transactional
 3277        editor.undo(&Undo, window, cx);
 3278        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3279        assert_eq!(
 3280            editor.selections.ranges::<Point>(cx),
 3281            &[Point::new(0, 5)..Point::new(2, 2)]
 3282        );
 3283
 3284        // When joining an empty line don't insert a space
 3285        editor.change_selections(None, window, cx, |s| {
 3286            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3287        });
 3288        editor.join_lines(&JoinLines, window, cx);
 3289        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3290        assert_eq!(
 3291            editor.selections.ranges::<Point>(cx),
 3292            [Point::new(2, 3)..Point::new(2, 3)]
 3293        );
 3294
 3295        // We can remove trailing newlines
 3296        editor.join_lines(&JoinLines, window, cx);
 3297        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3298        assert_eq!(
 3299            editor.selections.ranges::<Point>(cx),
 3300            [Point::new(2, 3)..Point::new(2, 3)]
 3301        );
 3302
 3303        // We don't blow up on the last line
 3304        editor.join_lines(&JoinLines, window, cx);
 3305        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3306        assert_eq!(
 3307            editor.selections.ranges::<Point>(cx),
 3308            [Point::new(2, 3)..Point::new(2, 3)]
 3309        );
 3310
 3311        // reset to test indentation
 3312        editor.buffer.update(cx, |buffer, cx| {
 3313            buffer.edit(
 3314                [
 3315                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3316                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3317                ],
 3318                None,
 3319                cx,
 3320            )
 3321        });
 3322
 3323        // We remove any leading spaces
 3324        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3325        editor.change_selections(None, window, cx, |s| {
 3326            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3327        });
 3328        editor.join_lines(&JoinLines, window, cx);
 3329        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3330
 3331        // We don't insert a space for a line containing only spaces
 3332        editor.join_lines(&JoinLines, window, cx);
 3333        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3334
 3335        // We ignore any leading tabs
 3336        editor.join_lines(&JoinLines, window, cx);
 3337        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3338
 3339        editor
 3340    });
 3341}
 3342
 3343#[gpui::test]
 3344fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3345    init_test(cx, |_| {});
 3346
 3347    cx.add_window(|window, cx| {
 3348        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3349        let mut editor = build_editor(buffer.clone(), window, cx);
 3350        let buffer = buffer.read(cx).as_singleton().unwrap();
 3351
 3352        editor.change_selections(None, window, cx, |s| {
 3353            s.select_ranges([
 3354                Point::new(0, 2)..Point::new(1, 1),
 3355                Point::new(1, 2)..Point::new(1, 2),
 3356                Point::new(3, 1)..Point::new(3, 2),
 3357            ])
 3358        });
 3359
 3360        editor.join_lines(&JoinLines, window, cx);
 3361        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3362
 3363        assert_eq!(
 3364            editor.selections.ranges::<Point>(cx),
 3365            [
 3366                Point::new(0, 7)..Point::new(0, 7),
 3367                Point::new(1, 3)..Point::new(1, 3)
 3368            ]
 3369        );
 3370        editor
 3371    });
 3372}
 3373
 3374#[gpui::test]
 3375async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3376    init_test(cx, |_| {});
 3377
 3378    let mut cx = EditorTestContext::new(cx).await;
 3379
 3380    let diff_base = r#"
 3381        Line 0
 3382        Line 1
 3383        Line 2
 3384        Line 3
 3385        "#
 3386    .unindent();
 3387
 3388    cx.set_state(
 3389        &r#"
 3390        ˇLine 0
 3391        Line 1
 3392        Line 2
 3393        Line 3
 3394        "#
 3395        .unindent(),
 3396    );
 3397
 3398    cx.set_head_text(&diff_base);
 3399    executor.run_until_parked();
 3400
 3401    // Join lines
 3402    cx.update_editor(|editor, window, cx| {
 3403        editor.join_lines(&JoinLines, window, cx);
 3404    });
 3405    executor.run_until_parked();
 3406
 3407    cx.assert_editor_state(
 3408        &r#"
 3409        Line 0ˇ Line 1
 3410        Line 2
 3411        Line 3
 3412        "#
 3413        .unindent(),
 3414    );
 3415    // Join again
 3416    cx.update_editor(|editor, window, cx| {
 3417        editor.join_lines(&JoinLines, window, cx);
 3418    });
 3419    executor.run_until_parked();
 3420
 3421    cx.assert_editor_state(
 3422        &r#"
 3423        Line 0 Line 1ˇ Line 2
 3424        Line 3
 3425        "#
 3426        .unindent(),
 3427    );
 3428}
 3429
 3430#[gpui::test]
 3431async fn test_custom_newlines_cause_no_false_positive_diffs(
 3432    executor: BackgroundExecutor,
 3433    cx: &mut TestAppContext,
 3434) {
 3435    init_test(cx, |_| {});
 3436    let mut cx = EditorTestContext::new(cx).await;
 3437    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3438    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3439    executor.run_until_parked();
 3440
 3441    cx.update_editor(|editor, window, cx| {
 3442        let snapshot = editor.snapshot(window, cx);
 3443        assert_eq!(
 3444            snapshot
 3445                .buffer_snapshot
 3446                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3447                .collect::<Vec<_>>(),
 3448            Vec::new(),
 3449            "Should not have any diffs for files with custom newlines"
 3450        );
 3451    });
 3452}
 3453
 3454#[gpui::test]
 3455async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3456    init_test(cx, |_| {});
 3457
 3458    let mut cx = EditorTestContext::new(cx).await;
 3459
 3460    // Test sort_lines_case_insensitive()
 3461    cx.set_state(indoc! {"
 3462        «z
 3463        y
 3464        x
 3465        Z
 3466        Y
 3467        Xˇ»
 3468    "});
 3469    cx.update_editor(|e, window, cx| {
 3470        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3471    });
 3472    cx.assert_editor_state(indoc! {"
 3473        «x
 3474        X
 3475        y
 3476        Y
 3477        z
 3478        Zˇ»
 3479    "});
 3480
 3481    // Test reverse_lines()
 3482    cx.set_state(indoc! {"
 3483        «5
 3484        4
 3485        3
 3486        2
 3487        1ˇ»
 3488    "});
 3489    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        «1
 3492        2
 3493        3
 3494        4
 3495        5ˇ»
 3496    "});
 3497
 3498    // Skip testing shuffle_line()
 3499
 3500    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3501    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3502
 3503    // Don't manipulate when cursor is on single line, but expand the selection
 3504    cx.set_state(indoc! {"
 3505        ddˇdd
 3506        ccc
 3507        bb
 3508        a
 3509    "});
 3510    cx.update_editor(|e, window, cx| {
 3511        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3512    });
 3513    cx.assert_editor_state(indoc! {"
 3514        «ddddˇ»
 3515        ccc
 3516        bb
 3517        a
 3518    "});
 3519
 3520    // Basic manipulate case
 3521    // Start selection moves to column 0
 3522    // End of selection shrinks to fit shorter line
 3523    cx.set_state(indoc! {"
 3524        dd«d
 3525        ccc
 3526        bb
 3527        aaaaaˇ»
 3528    "});
 3529    cx.update_editor(|e, window, cx| {
 3530        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3531    });
 3532    cx.assert_editor_state(indoc! {"
 3533        «aaaaa
 3534        bb
 3535        ccc
 3536        dddˇ»
 3537    "});
 3538
 3539    // Manipulate case with newlines
 3540    cx.set_state(indoc! {"
 3541        dd«d
 3542        ccc
 3543
 3544        bb
 3545        aaaaa
 3546
 3547        ˇ»
 3548    "});
 3549    cx.update_editor(|e, window, cx| {
 3550        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3551    });
 3552    cx.assert_editor_state(indoc! {"
 3553        «
 3554
 3555        aaaaa
 3556        bb
 3557        ccc
 3558        dddˇ»
 3559
 3560    "});
 3561
 3562    // Adding new line
 3563    cx.set_state(indoc! {"
 3564        aa«a
 3565        bbˇ»b
 3566    "});
 3567    cx.update_editor(|e, window, cx| {
 3568        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3569    });
 3570    cx.assert_editor_state(indoc! {"
 3571        «aaa
 3572        bbb
 3573        added_lineˇ»
 3574    "});
 3575
 3576    // Removing line
 3577    cx.set_state(indoc! {"
 3578        aa«a
 3579        bbbˇ»
 3580    "});
 3581    cx.update_editor(|e, window, cx| {
 3582        e.manipulate_lines(window, cx, |lines| {
 3583            lines.pop();
 3584        })
 3585    });
 3586    cx.assert_editor_state(indoc! {"
 3587        «aaaˇ»
 3588    "});
 3589
 3590    // Removing all lines
 3591    cx.set_state(indoc! {"
 3592        aa«a
 3593        bbbˇ»
 3594    "});
 3595    cx.update_editor(|e, window, cx| {
 3596        e.manipulate_lines(window, cx, |lines| {
 3597            lines.drain(..);
 3598        })
 3599    });
 3600    cx.assert_editor_state(indoc! {"
 3601        ˇ
 3602    "});
 3603}
 3604
 3605#[gpui::test]
 3606async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3607    init_test(cx, |_| {});
 3608
 3609    let mut cx = EditorTestContext::new(cx).await;
 3610
 3611    // Consider continuous selection as single selection
 3612    cx.set_state(indoc! {"
 3613        Aaa«aa
 3614        cˇ»c«c
 3615        bb
 3616        aaaˇ»aa
 3617    "});
 3618    cx.update_editor(|e, window, cx| {
 3619        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3620    });
 3621    cx.assert_editor_state(indoc! {"
 3622        «Aaaaa
 3623        ccc
 3624        bb
 3625        aaaaaˇ»
 3626    "});
 3627
 3628    cx.set_state(indoc! {"
 3629        Aaa«aa
 3630        cˇ»c«c
 3631        bb
 3632        aaaˇ»aa
 3633    "});
 3634    cx.update_editor(|e, window, cx| {
 3635        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3636    });
 3637    cx.assert_editor_state(indoc! {"
 3638        «Aaaaa
 3639        ccc
 3640        bbˇ»
 3641    "});
 3642
 3643    // Consider non continuous selection as distinct dedup operations
 3644    cx.set_state(indoc! {"
 3645        «aaaaa
 3646        bb
 3647        aaaaa
 3648        aaaaaˇ»
 3649
 3650        aaa«aaˇ»
 3651    "});
 3652    cx.update_editor(|e, window, cx| {
 3653        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3654    });
 3655    cx.assert_editor_state(indoc! {"
 3656        «aaaaa
 3657        bbˇ»
 3658
 3659        «aaaaaˇ»
 3660    "});
 3661}
 3662
 3663#[gpui::test]
 3664async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3665    init_test(cx, |_| {});
 3666
 3667    let mut cx = EditorTestContext::new(cx).await;
 3668
 3669    cx.set_state(indoc! {"
 3670        «Aaa
 3671        aAa
 3672        Aaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «Aaa
 3679        aAaˇ»
 3680    "});
 3681
 3682    cx.set_state(indoc! {"
 3683        «Aaa
 3684        aAa
 3685        aaAˇ»
 3686    "});
 3687    cx.update_editor(|e, window, cx| {
 3688        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        «Aaaˇ»
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Manipulate with multiple selections on a single line
 3702    cx.set_state(indoc! {"
 3703        dd«dd
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «aaaaa
 3713        bb
 3714        ccc
 3715        ddddˇ»
 3716    "});
 3717
 3718    // Manipulate with multiple disjoin selections
 3719    cx.set_state(indoc! {"
 3720 3721        4
 3722        3
 3723        2
 3724        1ˇ»
 3725
 3726        dd«dd
 3727        ccc
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «1
 3736        2
 3737        3
 3738        4
 3739        5ˇ»
 3740
 3741        «aaaaa
 3742        bb
 3743        ccc
 3744        ddddˇ»
 3745    "});
 3746
 3747    // Adding lines on each selection
 3748    cx.set_state(indoc! {"
 3749 3750        1ˇ»
 3751
 3752        bb«bb
 3753        aaaˇ»aa
 3754    "});
 3755    cx.update_editor(|e, window, cx| {
 3756        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3757    });
 3758    cx.assert_editor_state(indoc! {"
 3759        «2
 3760        1
 3761        added lineˇ»
 3762
 3763        «bbbb
 3764        aaaaa
 3765        added lineˇ»
 3766    "});
 3767
 3768    // Removing lines on each selection
 3769    cx.set_state(indoc! {"
 3770 3771        1ˇ»
 3772
 3773        bb«bb
 3774        aaaˇ»aa
 3775    "});
 3776    cx.update_editor(|e, window, cx| {
 3777        e.manipulate_lines(window, cx, |lines| {
 3778            lines.pop();
 3779        })
 3780    });
 3781    cx.assert_editor_state(indoc! {"
 3782        «2ˇ»
 3783
 3784        «bbbbˇ»
 3785    "});
 3786}
 3787
 3788#[gpui::test]
 3789async fn test_manipulate_text(cx: &mut TestAppContext) {
 3790    init_test(cx, |_| {});
 3791
 3792    let mut cx = EditorTestContext::new(cx).await;
 3793
 3794    // Test convert_to_upper_case()
 3795    cx.set_state(indoc! {"
 3796        «hello worldˇ»
 3797    "});
 3798    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3799    cx.assert_editor_state(indoc! {"
 3800        «HELLO WORLDˇ»
 3801    "});
 3802
 3803    // Test convert_to_lower_case()
 3804    cx.set_state(indoc! {"
 3805        «HELLO WORLDˇ»
 3806    "});
 3807    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3808    cx.assert_editor_state(indoc! {"
 3809        «hello worldˇ»
 3810    "});
 3811
 3812    // Test multiple line, single selection case
 3813    cx.set_state(indoc! {"
 3814        «The quick brown
 3815        fox jumps over
 3816        the lazy dogˇ»
 3817    "});
 3818    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3819    cx.assert_editor_state(indoc! {"
 3820        «The Quick Brown
 3821        Fox Jumps Over
 3822        The Lazy Dogˇ»
 3823    "});
 3824
 3825    // Test multiple line, single selection case
 3826    cx.set_state(indoc! {"
 3827        «The quick brown
 3828        fox jumps over
 3829        the lazy dogˇ»
 3830    "});
 3831    cx.update_editor(|e, window, cx| {
 3832        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3833    });
 3834    cx.assert_editor_state(indoc! {"
 3835        «TheQuickBrown
 3836        FoxJumpsOver
 3837        TheLazyDogˇ»
 3838    "});
 3839
 3840    // From here on out, test more complex cases of manipulate_text()
 3841
 3842    // Test no selection case - should affect words cursors are in
 3843    // Cursor at beginning, middle, and end of word
 3844    cx.set_state(indoc! {"
 3845        ˇhello big beauˇtiful worldˇ
 3846    "});
 3847    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3848    cx.assert_editor_state(indoc! {"
 3849        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3850    "});
 3851
 3852    // Test multiple selections on a single line and across multiple lines
 3853    cx.set_state(indoc! {"
 3854        «Theˇ» quick «brown
 3855        foxˇ» jumps «overˇ»
 3856        the «lazyˇ» dog
 3857    "});
 3858    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3859    cx.assert_editor_state(indoc! {"
 3860        «THEˇ» quick «BROWN
 3861        FOXˇ» jumps «OVERˇ»
 3862        the «LAZYˇ» dog
 3863    "});
 3864
 3865    // Test case where text length grows
 3866    cx.set_state(indoc! {"
 3867        «tschüߡ»
 3868    "});
 3869    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3870    cx.assert_editor_state(indoc! {"
 3871        «TSCHÜSSˇ»
 3872    "});
 3873
 3874    // Test to make sure we don't crash when text shrinks
 3875    cx.set_state(indoc! {"
 3876        aaa_bbbˇ
 3877    "});
 3878    cx.update_editor(|e, window, cx| {
 3879        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3880    });
 3881    cx.assert_editor_state(indoc! {"
 3882        «aaaBbbˇ»
 3883    "});
 3884
 3885    // Test to make sure we all aware of the fact that each word can grow and shrink
 3886    // Final selections should be aware of this fact
 3887    cx.set_state(indoc! {"
 3888        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3889    "});
 3890    cx.update_editor(|e, window, cx| {
 3891        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3892    });
 3893    cx.assert_editor_state(indoc! {"
 3894        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3895    "});
 3896
 3897    cx.set_state(indoc! {"
 3898        «hElLo, WoRld!ˇ»
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «HeLlO, wOrLD!ˇ»
 3905    "});
 3906}
 3907
 3908#[gpui::test]
 3909fn test_duplicate_line(cx: &mut TestAppContext) {
 3910    init_test(cx, |_| {});
 3911
 3912    let editor = cx.add_window(|window, cx| {
 3913        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3914        build_editor(buffer, window, cx)
 3915    });
 3916    _ = editor.update(cx, |editor, window, cx| {
 3917        editor.change_selections(None, window, cx, |s| {
 3918            s.select_display_ranges([
 3919                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3920                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3921                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3922                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3923            ])
 3924        });
 3925        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3926        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3927        assert_eq!(
 3928            editor.selections.display_ranges(cx),
 3929            vec![
 3930                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3931                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3932                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3933                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3934            ]
 3935        );
 3936    });
 3937
 3938    let editor = cx.add_window(|window, cx| {
 3939        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3940        build_editor(buffer, window, cx)
 3941    });
 3942    _ = editor.update(cx, |editor, window, cx| {
 3943        editor.change_selections(None, window, cx, |s| {
 3944            s.select_display_ranges([
 3945                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3946                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3947            ])
 3948        });
 3949        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3950        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3951        assert_eq!(
 3952            editor.selections.display_ranges(cx),
 3953            vec![
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3955                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3956            ]
 3957        );
 3958    });
 3959
 3960    // With `move_upwards` the selections stay in place, except for
 3961    // the lines inserted above them
 3962    let editor = cx.add_window(|window, cx| {
 3963        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3964        build_editor(buffer, window, cx)
 3965    });
 3966    _ = editor.update(cx, |editor, window, cx| {
 3967        editor.change_selections(None, window, cx, |s| {
 3968            s.select_display_ranges([
 3969                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3970                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3971                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3972                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3973            ])
 3974        });
 3975        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3976        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3977        assert_eq!(
 3978            editor.selections.display_ranges(cx),
 3979            vec![
 3980                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3981                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3982                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3983                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3984            ]
 3985        );
 3986    });
 3987
 3988    let editor = cx.add_window(|window, cx| {
 3989        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3990        build_editor(buffer, window, cx)
 3991    });
 3992    _ = editor.update(cx, |editor, window, cx| {
 3993        editor.change_selections(None, window, cx, |s| {
 3994            s.select_display_ranges([
 3995                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3996                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3997            ])
 3998        });
 3999        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4000        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4001        assert_eq!(
 4002            editor.selections.display_ranges(cx),
 4003            vec![
 4004                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4005                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4006            ]
 4007        );
 4008    });
 4009
 4010    let editor = cx.add_window(|window, cx| {
 4011        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4012        build_editor(buffer, window, cx)
 4013    });
 4014    _ = editor.update(cx, |editor, window, cx| {
 4015        editor.change_selections(None, window, cx, |s| {
 4016            s.select_display_ranges([
 4017                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4018                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4019            ])
 4020        });
 4021        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4022        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4023        assert_eq!(
 4024            editor.selections.display_ranges(cx),
 4025            vec![
 4026                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4027                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4028            ]
 4029        );
 4030    });
 4031}
 4032
 4033#[gpui::test]
 4034fn test_move_line_up_down(cx: &mut TestAppContext) {
 4035    init_test(cx, |_| {});
 4036
 4037    let editor = cx.add_window(|window, cx| {
 4038        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4039        build_editor(buffer, window, cx)
 4040    });
 4041    _ = editor.update(cx, |editor, window, cx| {
 4042        editor.fold_creases(
 4043            vec![
 4044                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4045                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4046                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4047            ],
 4048            true,
 4049            window,
 4050            cx,
 4051        );
 4052        editor.change_selections(None, window, cx, |s| {
 4053            s.select_display_ranges([
 4054                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4055                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4056                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4057                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4058            ])
 4059        });
 4060        assert_eq!(
 4061            editor.display_text(cx),
 4062            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4063        );
 4064
 4065        editor.move_line_up(&MoveLineUp, window, cx);
 4066        assert_eq!(
 4067            editor.display_text(cx),
 4068            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4069        );
 4070        assert_eq!(
 4071            editor.selections.display_ranges(cx),
 4072            vec![
 4073                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4074                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4075                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4076                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4077            ]
 4078        );
 4079    });
 4080
 4081    _ = editor.update(cx, |editor, window, cx| {
 4082        editor.move_line_down(&MoveLineDown, window, cx);
 4083        assert_eq!(
 4084            editor.display_text(cx),
 4085            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4086        );
 4087        assert_eq!(
 4088            editor.selections.display_ranges(cx),
 4089            vec![
 4090                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4091                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4092                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4093                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4094            ]
 4095        );
 4096    });
 4097
 4098    _ = editor.update(cx, |editor, window, cx| {
 4099        editor.move_line_down(&MoveLineDown, window, cx);
 4100        assert_eq!(
 4101            editor.display_text(cx),
 4102            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4103        );
 4104        assert_eq!(
 4105            editor.selections.display_ranges(cx),
 4106            vec![
 4107                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4108                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4109                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4110                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4111            ]
 4112        );
 4113    });
 4114
 4115    _ = editor.update(cx, |editor, window, cx| {
 4116        editor.move_line_up(&MoveLineUp, window, cx);
 4117        assert_eq!(
 4118            editor.display_text(cx),
 4119            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4120        );
 4121        assert_eq!(
 4122            editor.selections.display_ranges(cx),
 4123            vec![
 4124                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4125                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4126                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4127                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4128            ]
 4129        );
 4130    });
 4131}
 4132
 4133#[gpui::test]
 4134fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4135    init_test(cx, |_| {});
 4136
 4137    let editor = cx.add_window(|window, cx| {
 4138        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4139        build_editor(buffer, window, cx)
 4140    });
 4141    _ = editor.update(cx, |editor, window, cx| {
 4142        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4143        editor.insert_blocks(
 4144            [BlockProperties {
 4145                style: BlockStyle::Fixed,
 4146                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4147                height: 1,
 4148                render: Arc::new(|_| div().into_any()),
 4149                priority: 0,
 4150            }],
 4151            Some(Autoscroll::fit()),
 4152            cx,
 4153        );
 4154        editor.change_selections(None, window, cx, |s| {
 4155            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4156        });
 4157        editor.move_line_down(&MoveLineDown, window, cx);
 4158    });
 4159}
 4160
 4161#[gpui::test]
 4162async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4163    init_test(cx, |_| {});
 4164
 4165    let mut cx = EditorTestContext::new(cx).await;
 4166    cx.set_state(
 4167        &"
 4168            ˇzero
 4169            one
 4170            two
 4171            three
 4172            four
 4173            five
 4174        "
 4175        .unindent(),
 4176    );
 4177
 4178    // Create a four-line block that replaces three lines of text.
 4179    cx.update_editor(|editor, window, cx| {
 4180        let snapshot = editor.snapshot(window, cx);
 4181        let snapshot = &snapshot.buffer_snapshot;
 4182        let placement = BlockPlacement::Replace(
 4183            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4184        );
 4185        editor.insert_blocks(
 4186            [BlockProperties {
 4187                placement,
 4188                height: 4,
 4189                style: BlockStyle::Sticky,
 4190                render: Arc::new(|_| gpui::div().into_any_element()),
 4191                priority: 0,
 4192            }],
 4193            None,
 4194            cx,
 4195        );
 4196    });
 4197
 4198    // Move down so that the cursor touches the block.
 4199    cx.update_editor(|editor, window, cx| {
 4200        editor.move_down(&Default::default(), window, cx);
 4201    });
 4202    cx.assert_editor_state(
 4203        &"
 4204            zero
 4205            «one
 4206            two
 4207            threeˇ»
 4208            four
 4209            five
 4210        "
 4211        .unindent(),
 4212    );
 4213
 4214    // Move down past the block.
 4215    cx.update_editor(|editor, window, cx| {
 4216        editor.move_down(&Default::default(), window, cx);
 4217    });
 4218    cx.assert_editor_state(
 4219        &"
 4220            zero
 4221            one
 4222            two
 4223            three
 4224            ˇfour
 4225            five
 4226        "
 4227        .unindent(),
 4228    );
 4229}
 4230
 4231#[gpui::test]
 4232fn test_transpose(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    _ = cx.add_window(|window, cx| {
 4236        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4237        editor.set_style(EditorStyle::default(), window, cx);
 4238        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4239        editor.transpose(&Default::default(), window, cx);
 4240        assert_eq!(editor.text(cx), "bac");
 4241        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4242
 4243        editor.transpose(&Default::default(), window, cx);
 4244        assert_eq!(editor.text(cx), "bca");
 4245        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4246
 4247        editor.transpose(&Default::default(), window, cx);
 4248        assert_eq!(editor.text(cx), "bac");
 4249        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4250
 4251        editor
 4252    });
 4253
 4254    _ = cx.add_window(|window, cx| {
 4255        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4256        editor.set_style(EditorStyle::default(), window, cx);
 4257        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4258        editor.transpose(&Default::default(), window, cx);
 4259        assert_eq!(editor.text(cx), "acb\nde");
 4260        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4261
 4262        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4263        editor.transpose(&Default::default(), window, cx);
 4264        assert_eq!(editor.text(cx), "acbd\ne");
 4265        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4266
 4267        editor.transpose(&Default::default(), window, cx);
 4268        assert_eq!(editor.text(cx), "acbde\n");
 4269        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4270
 4271        editor.transpose(&Default::default(), window, cx);
 4272        assert_eq!(editor.text(cx), "acbd\ne");
 4273        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4274
 4275        editor
 4276    });
 4277
 4278    _ = cx.add_window(|window, cx| {
 4279        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4280        editor.set_style(EditorStyle::default(), window, cx);
 4281        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4282        editor.transpose(&Default::default(), window, cx);
 4283        assert_eq!(editor.text(cx), "bacd\ne");
 4284        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4285
 4286        editor.transpose(&Default::default(), window, cx);
 4287        assert_eq!(editor.text(cx), "bcade\n");
 4288        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4289
 4290        editor.transpose(&Default::default(), window, cx);
 4291        assert_eq!(editor.text(cx), "bcda\ne");
 4292        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4293
 4294        editor.transpose(&Default::default(), window, cx);
 4295        assert_eq!(editor.text(cx), "bcade\n");
 4296        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4297
 4298        editor.transpose(&Default::default(), window, cx);
 4299        assert_eq!(editor.text(cx), "bcaed\n");
 4300        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4301
 4302        editor
 4303    });
 4304
 4305    _ = cx.add_window(|window, cx| {
 4306        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4307        editor.set_style(EditorStyle::default(), window, cx);
 4308        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4309        editor.transpose(&Default::default(), window, cx);
 4310        assert_eq!(editor.text(cx), "🏀🍐✋");
 4311        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4312
 4313        editor.transpose(&Default::default(), window, cx);
 4314        assert_eq!(editor.text(cx), "🏀✋🍐");
 4315        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4316
 4317        editor.transpose(&Default::default(), window, cx);
 4318        assert_eq!(editor.text(cx), "🏀🍐✋");
 4319        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4320
 4321        editor
 4322    });
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_rewrap(cx: &mut TestAppContext) {
 4327    init_test(cx, |settings| {
 4328        settings.languages.extend([
 4329            (
 4330                "Markdown".into(),
 4331                LanguageSettingsContent {
 4332                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4333                    ..Default::default()
 4334                },
 4335            ),
 4336            (
 4337                "Plain Text".into(),
 4338                LanguageSettingsContent {
 4339                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4340                    ..Default::default()
 4341                },
 4342            ),
 4343        ])
 4344    });
 4345
 4346    let mut cx = EditorTestContext::new(cx).await;
 4347
 4348    let language_with_c_comments = Arc::new(Language::new(
 4349        LanguageConfig {
 4350            line_comments: vec!["// ".into()],
 4351            ..LanguageConfig::default()
 4352        },
 4353        None,
 4354    ));
 4355    let language_with_pound_comments = Arc::new(Language::new(
 4356        LanguageConfig {
 4357            line_comments: vec!["# ".into()],
 4358            ..LanguageConfig::default()
 4359        },
 4360        None,
 4361    ));
 4362    let markdown_language = Arc::new(Language::new(
 4363        LanguageConfig {
 4364            name: "Markdown".into(),
 4365            ..LanguageConfig::default()
 4366        },
 4367        None,
 4368    ));
 4369    let language_with_doc_comments = Arc::new(Language::new(
 4370        LanguageConfig {
 4371            line_comments: vec!["// ".into(), "/// ".into()],
 4372            ..LanguageConfig::default()
 4373        },
 4374        Some(tree_sitter_rust::LANGUAGE.into()),
 4375    ));
 4376
 4377    let plaintext_language = Arc::new(Language::new(
 4378        LanguageConfig {
 4379            name: "Plain Text".into(),
 4380            ..LanguageConfig::default()
 4381        },
 4382        None,
 4383    ));
 4384
 4385    assert_rewrap(
 4386        indoc! {"
 4387            // ˇ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.
 4388        "},
 4389        indoc! {"
 4390            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4391            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4392            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4393            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4394            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4395            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4396            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4397            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4398            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4399            // porttitor id. Aliquam id accumsan eros.
 4400        "},
 4401        language_with_c_comments.clone(),
 4402        &mut cx,
 4403    );
 4404
 4405    // Test that rewrapping works inside of a selection
 4406    assert_rewrap(
 4407        indoc! {"
 4408            «// 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.ˇ»
 4409        "},
 4410        indoc! {"
 4411            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4412            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4413            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4414            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4415            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4416            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4417            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4418            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4419            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4420            // porttitor id. Aliquam id accumsan eros.ˇ»
 4421        "},
 4422        language_with_c_comments.clone(),
 4423        &mut cx,
 4424    );
 4425
 4426    // Test that cursors that expand to the same region are collapsed.
 4427    assert_rewrap(
 4428        indoc! {"
 4429            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4430            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4431            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4432            // ˇ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.
 4433        "},
 4434        indoc! {"
 4435            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4436            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4437            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4438            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4439            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4440            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4441            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4442            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4443            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4444            // porttitor id. Aliquam id accumsan eros.
 4445        "},
 4446        language_with_c_comments.clone(),
 4447        &mut cx,
 4448    );
 4449
 4450    // Test that non-contiguous selections are treated separately.
 4451    assert_rewrap(
 4452        indoc! {"
 4453            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4454            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4455            //
 4456            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4457            // ˇ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.
 4458        "},
 4459        indoc! {"
 4460            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4461            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4462            // auctor, eu lacinia sapien scelerisque.
 4463            //
 4464            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4465            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4466            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4467            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4468            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4469            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4470            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4471        "},
 4472        language_with_c_comments.clone(),
 4473        &mut cx,
 4474    );
 4475
 4476    // Test that different comment prefixes are supported.
 4477    assert_rewrap(
 4478        indoc! {"
 4479            # ˇ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.
 4480        "},
 4481        indoc! {"
 4482            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4483            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4484            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4485            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4486            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4487            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4488            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4489            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4490            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4491            # accumsan eros.
 4492        "},
 4493        language_with_pound_comments.clone(),
 4494        &mut cx,
 4495    );
 4496
 4497    // Test that rewrapping is ignored outside of comments in most languages.
 4498    assert_rewrap(
 4499        indoc! {"
 4500            /// Adds two numbers.
 4501            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4502            fn add(a: u32, b: u32) -> u32 {
 4503                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ˇ
 4504            }
 4505        "},
 4506        indoc! {"
 4507            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4508            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4509            fn add(a: u32, b: u32) -> u32 {
 4510                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ˇ
 4511            }
 4512        "},
 4513        language_with_doc_comments.clone(),
 4514        &mut cx,
 4515    );
 4516
 4517    // Test that rewrapping works in Markdown and Plain Text languages.
 4518    assert_rewrap(
 4519        indoc! {"
 4520            # Hello
 4521
 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            # Hello
 4526
 4527            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4528            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4529            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4530            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4531            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4532            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4533            Integer sit amet scelerisque nisi.
 4534        "},
 4535        markdown_language,
 4536        &mut cx,
 4537    );
 4538
 4539    assert_rewrap(
 4540        indoc! {"
 4541            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.
 4542        "},
 4543        indoc! {"
 4544            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4545            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4546            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4547            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4548            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4549            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4550            Integer sit amet scelerisque nisi.
 4551        "},
 4552        plaintext_language,
 4553        &mut cx,
 4554    );
 4555
 4556    // Test rewrapping unaligned comments in a selection.
 4557    assert_rewrap(
 4558        indoc! {"
 4559            fn foo() {
 4560                if true {
 4561            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4562            // Praesent semper egestas tellus id dignissim.ˇ»
 4563                    do_something();
 4564                } else {
 4565                    //
 4566                }
 4567            }
 4568        "},
 4569        indoc! {"
 4570            fn foo() {
 4571                if true {
 4572            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4573                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4574                    // egestas tellus id dignissim.ˇ»
 4575                    do_something();
 4576                } else {
 4577                    //
 4578                }
 4579            }
 4580        "},
 4581        language_with_doc_comments.clone(),
 4582        &mut cx,
 4583    );
 4584
 4585    assert_rewrap(
 4586        indoc! {"
 4587            fn foo() {
 4588                if true {
 4589            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4590            // Praesent semper egestas tellus id dignissim.»
 4591                    do_something();
 4592                } else {
 4593                    //
 4594                }
 4595
 4596            }
 4597        "},
 4598        indoc! {"
 4599            fn foo() {
 4600                if true {
 4601            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4602                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4603                    // egestas tellus id dignissim.»
 4604                    do_something();
 4605                } else {
 4606                    //
 4607                }
 4608
 4609            }
 4610        "},
 4611        language_with_doc_comments.clone(),
 4612        &mut cx,
 4613    );
 4614
 4615    #[track_caller]
 4616    fn assert_rewrap(
 4617        unwrapped_text: &str,
 4618        wrapped_text: &str,
 4619        language: Arc<Language>,
 4620        cx: &mut EditorTestContext,
 4621    ) {
 4622        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4623        cx.set_state(unwrapped_text);
 4624        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4625        cx.assert_editor_state(wrapped_text);
 4626    }
 4627}
 4628
 4629#[gpui::test]
 4630async fn test_clipboard(cx: &mut TestAppContext) {
 4631    init_test(cx, |_| {});
 4632
 4633    let mut cx = EditorTestContext::new(cx).await;
 4634
 4635    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4636    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4637    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4638
 4639    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4640    cx.set_state("two ˇfour ˇsix ˇ");
 4641    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4642    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4643
 4644    // Paste again but with only two cursors. Since the number of cursors doesn't
 4645    // match the number of slices in the clipboard, the entire clipboard text
 4646    // is pasted at each cursor.
 4647    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4648    cx.update_editor(|e, window, cx| {
 4649        e.handle_input("( ", window, cx);
 4650        e.paste(&Paste, window, cx);
 4651        e.handle_input(") ", window, cx);
 4652    });
 4653    cx.assert_editor_state(
 4654        &([
 4655            "( one✅ ",
 4656            "three ",
 4657            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4658            "three ",
 4659            "five ) ˇ",
 4660        ]
 4661        .join("\n")),
 4662    );
 4663
 4664    // Cut with three selections, one of which is full-line.
 4665    cx.set_state(indoc! {"
 4666        1«2ˇ»3
 4667        4ˇ567
 4668        «8ˇ»9"});
 4669    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4670    cx.assert_editor_state(indoc! {"
 4671        1ˇ3
 4672        ˇ9"});
 4673
 4674    // Paste with three selections, noticing how the copied selection that was full-line
 4675    // gets inserted before the second cursor.
 4676    cx.set_state(indoc! {"
 4677        1ˇ3
 4678 4679        «oˇ»ne"});
 4680    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4681    cx.assert_editor_state(indoc! {"
 4682        12ˇ3
 4683        4567
 4684 4685        8ˇne"});
 4686
 4687    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4688    cx.set_state(indoc! {"
 4689        The quick brown
 4690        fox juˇmps over
 4691        the lazy dog"});
 4692    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4693    assert_eq!(
 4694        cx.read_from_clipboard()
 4695            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4696        Some("fox jumps over\n".to_string())
 4697    );
 4698
 4699    // Paste with three selections, noticing how the copied full-line selection is inserted
 4700    // before the empty selections but replaces the selection that is non-empty.
 4701    cx.set_state(indoc! {"
 4702        Tˇhe quick brown
 4703        «foˇ»x jumps over
 4704        tˇhe lazy dog"});
 4705    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4706    cx.assert_editor_state(indoc! {"
 4707        fox jumps over
 4708        Tˇhe quick brown
 4709        fox jumps over
 4710        ˇx jumps over
 4711        fox jumps over
 4712        tˇhe lazy dog"});
 4713}
 4714
 4715#[gpui::test]
 4716async fn test_paste_multiline(cx: &mut TestAppContext) {
 4717    init_test(cx, |_| {});
 4718
 4719    let mut cx = EditorTestContext::new(cx).await;
 4720    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4721
 4722    // Cut an indented block, without the leading whitespace.
 4723    cx.set_state(indoc! {"
 4724        const a: B = (
 4725            c(),
 4726            «d(
 4727                e,
 4728                f
 4729            )ˇ»
 4730        );
 4731    "});
 4732    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4733    cx.assert_editor_state(indoc! {"
 4734        const a: B = (
 4735            c(),
 4736            ˇ
 4737        );
 4738    "});
 4739
 4740    // Paste it at the same position.
 4741    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4742    cx.assert_editor_state(indoc! {"
 4743        const a: B = (
 4744            c(),
 4745            d(
 4746                e,
 4747                f
 4748 4749        );
 4750    "});
 4751
 4752    // Paste it at a line with a lower indent level.
 4753    cx.set_state(indoc! {"
 4754        ˇ
 4755        const a: B = (
 4756            c(),
 4757        );
 4758    "});
 4759    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4760    cx.assert_editor_state(indoc! {"
 4761        d(
 4762            e,
 4763            f
 4764 4765        const a: B = (
 4766            c(),
 4767        );
 4768    "});
 4769
 4770    // Cut an indented block, with the leading whitespace.
 4771    cx.set_state(indoc! {"
 4772        const a: B = (
 4773            c(),
 4774        «    d(
 4775                e,
 4776                f
 4777            )
 4778        ˇ»);
 4779    "});
 4780    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4781    cx.assert_editor_state(indoc! {"
 4782        const a: B = (
 4783            c(),
 4784        ˇ);
 4785    "});
 4786
 4787    // Paste it at the same position.
 4788    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4789    cx.assert_editor_state(indoc! {"
 4790        const a: B = (
 4791            c(),
 4792            d(
 4793                e,
 4794                f
 4795            )
 4796        ˇ);
 4797    "});
 4798
 4799    // Paste it at a line with a higher indent level.
 4800    cx.set_state(indoc! {"
 4801        const a: B = (
 4802            c(),
 4803            d(
 4804                e,
 4805 4806            )
 4807        );
 4808    "});
 4809    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4810    cx.assert_editor_state(indoc! {"
 4811        const a: B = (
 4812            c(),
 4813            d(
 4814                e,
 4815                f    d(
 4816                    e,
 4817                    f
 4818                )
 4819        ˇ
 4820            )
 4821        );
 4822    "});
 4823}
 4824
 4825#[gpui::test]
 4826async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4827    init_test(cx, |_| {});
 4828
 4829    cx.write_to_clipboard(ClipboardItem::new_string(
 4830        "    d(\n        e\n    );\n".into(),
 4831    ));
 4832
 4833    let mut cx = EditorTestContext::new(cx).await;
 4834    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4835
 4836    cx.set_state(indoc! {"
 4837        fn a() {
 4838            b();
 4839            if c() {
 4840                ˇ
 4841            }
 4842        }
 4843    "});
 4844
 4845    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4846    cx.assert_editor_state(indoc! {"
 4847        fn a() {
 4848            b();
 4849            if c() {
 4850                d(
 4851                    e
 4852                );
 4853        ˇ
 4854            }
 4855        }
 4856    "});
 4857
 4858    cx.set_state(indoc! {"
 4859        fn a() {
 4860            b();
 4861            ˇ
 4862        }
 4863    "});
 4864
 4865    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4866    cx.assert_editor_state(indoc! {"
 4867        fn a() {
 4868            b();
 4869            d(
 4870                e
 4871            );
 4872        ˇ
 4873        }
 4874    "});
 4875}
 4876
 4877#[gpui::test]
 4878fn test_select_all(cx: &mut TestAppContext) {
 4879    init_test(cx, |_| {});
 4880
 4881    let editor = cx.add_window(|window, cx| {
 4882        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4883        build_editor(buffer, window, cx)
 4884    });
 4885    _ = editor.update(cx, |editor, window, cx| {
 4886        editor.select_all(&SelectAll, window, cx);
 4887        assert_eq!(
 4888            editor.selections.display_ranges(cx),
 4889            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4890        );
 4891    });
 4892}
 4893
 4894#[gpui::test]
 4895fn test_select_line(cx: &mut TestAppContext) {
 4896    init_test(cx, |_| {});
 4897
 4898    let editor = cx.add_window(|window, cx| {
 4899        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4900        build_editor(buffer, window, cx)
 4901    });
 4902    _ = editor.update(cx, |editor, window, cx| {
 4903        editor.change_selections(None, window, cx, |s| {
 4904            s.select_display_ranges([
 4905                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4906                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4907                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4908                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4909            ])
 4910        });
 4911        editor.select_line(&SelectLine, window, cx);
 4912        assert_eq!(
 4913            editor.selections.display_ranges(cx),
 4914            vec![
 4915                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4916                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4917            ]
 4918        );
 4919    });
 4920
 4921    _ = editor.update(cx, |editor, window, cx| {
 4922        editor.select_line(&SelectLine, window, cx);
 4923        assert_eq!(
 4924            editor.selections.display_ranges(cx),
 4925            vec![
 4926                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4927                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4928            ]
 4929        );
 4930    });
 4931
 4932    _ = editor.update(cx, |editor, window, cx| {
 4933        editor.select_line(&SelectLine, window, cx);
 4934        assert_eq!(
 4935            editor.selections.display_ranges(cx),
 4936            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4937        );
 4938    });
 4939}
 4940
 4941#[gpui::test]
 4942async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4943    init_test(cx, |_| {});
 4944    let mut cx = EditorTestContext::new(cx).await;
 4945
 4946    #[track_caller]
 4947    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4948        cx.set_state(initial_state);
 4949        cx.update_editor(|e, window, cx| {
 4950            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4951        });
 4952        cx.assert_editor_state(expected_state);
 4953    }
 4954
 4955    // Selection starts and ends at the middle of lines, left-to-right
 4956    test(
 4957        &mut cx,
 4958        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4959        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4960    );
 4961    // Same thing, right-to-left
 4962    test(
 4963        &mut cx,
 4964        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4965        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4966    );
 4967
 4968    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4969    test(
 4970        &mut cx,
 4971        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4972        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4973    );
 4974    // Same thing, right-to-left
 4975    test(
 4976        &mut cx,
 4977        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4978        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4979    );
 4980
 4981    // Whole buffer, left-to-right, last line ends with newline
 4982    test(
 4983        &mut cx,
 4984        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4985        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4986    );
 4987    // Same thing, right-to-left
 4988    test(
 4989        &mut cx,
 4990        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4991        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4992    );
 4993
 4994    // Starts at the end of a line, ends at the start of another
 4995    test(
 4996        &mut cx,
 4997        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4998        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4999    );
 5000}
 5001
 5002#[gpui::test]
 5003async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5004    init_test(cx, |_| {});
 5005
 5006    let editor = cx.add_window(|window, cx| {
 5007        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5008        build_editor(buffer, window, cx)
 5009    });
 5010
 5011    // setup
 5012    _ = editor.update(cx, |editor, window, cx| {
 5013        editor.fold_creases(
 5014            vec![
 5015                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5016                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5017                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5018            ],
 5019            true,
 5020            window,
 5021            cx,
 5022        );
 5023        assert_eq!(
 5024            editor.display_text(cx),
 5025            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5026        );
 5027    });
 5028
 5029    _ = editor.update(cx, |editor, window, cx| {
 5030        editor.change_selections(None, window, cx, |s| {
 5031            s.select_display_ranges([
 5032                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5033                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5034                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5035                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5036            ])
 5037        });
 5038        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5039        assert_eq!(
 5040            editor.display_text(cx),
 5041            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5042        );
 5043    });
 5044    EditorTestContext::for_editor(editor, cx)
 5045        .await
 5046        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5047
 5048    _ = editor.update(cx, |editor, window, cx| {
 5049        editor.change_selections(None, window, cx, |s| {
 5050            s.select_display_ranges([
 5051                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5052            ])
 5053        });
 5054        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5055        assert_eq!(
 5056            editor.display_text(cx),
 5057            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5058        );
 5059        assert_eq!(
 5060            editor.selections.display_ranges(cx),
 5061            [
 5062                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5063                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5064                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5065                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5066                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5067                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5068                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5069            ]
 5070        );
 5071    });
 5072    EditorTestContext::for_editor(editor, cx)
 5073        .await
 5074        .assert_editor_state(
 5075            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5076        );
 5077}
 5078
 5079#[gpui::test]
 5080async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5081    init_test(cx, |_| {});
 5082
 5083    let mut cx = EditorTestContext::new(cx).await;
 5084
 5085    cx.set_state(indoc!(
 5086        r#"abc
 5087           defˇghi
 5088
 5089           jk
 5090           nlmo
 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ˇghi
 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ˇghi
 5114
 5115            jk
 5116            nlmo
 5117            "#
 5118    ));
 5119
 5120    cx.update_editor(|editor, window, cx| {
 5121        editor.add_selection_below(&Default::default(), window, cx);
 5122    });
 5123
 5124    cx.assert_editor_state(indoc!(
 5125        r#"abc
 5126           defˇghi
 5127
 5128           jk
 5129           nlmo
 5130           "#
 5131    ));
 5132
 5133    cx.update_editor(|editor, window, cx| {
 5134        editor.undo_selection(&Default::default(), window, cx);
 5135    });
 5136
 5137    cx.assert_editor_state(indoc!(
 5138        r#"abcˇ
 5139           defˇghi
 5140
 5141           jk
 5142           nlmo
 5143           "#
 5144    ));
 5145
 5146    cx.update_editor(|editor, window, cx| {
 5147        editor.redo_selection(&Default::default(), window, cx);
 5148    });
 5149
 5150    cx.assert_editor_state(indoc!(
 5151        r#"abc
 5152           defˇghi
 5153
 5154           jk
 5155           nlmo
 5156           "#
 5157    ));
 5158
 5159    cx.update_editor(|editor, window, cx| {
 5160        editor.add_selection_below(&Default::default(), window, cx);
 5161    });
 5162
 5163    cx.assert_editor_state(indoc!(
 5164        r#"abc
 5165           defˇghi
 5166
 5167           jk
 5168           nlmˇo
 5169           "#
 5170    ));
 5171
 5172    cx.update_editor(|editor, window, cx| {
 5173        editor.add_selection_below(&Default::default(), window, cx);
 5174    });
 5175
 5176    cx.assert_editor_state(indoc!(
 5177        r#"abc
 5178           defˇghi
 5179
 5180           jk
 5181           nlmˇo
 5182           "#
 5183    ));
 5184
 5185    // change selections
 5186    cx.set_state(indoc!(
 5187        r#"abc
 5188           def«ˇg»hi
 5189
 5190           jk
 5191           nlmo
 5192           "#
 5193    ));
 5194
 5195    cx.update_editor(|editor, window, cx| {
 5196        editor.add_selection_below(&Default::default(), window, cx);
 5197    });
 5198
 5199    cx.assert_editor_state(indoc!(
 5200        r#"abc
 5201           def«ˇg»hi
 5202
 5203           jk
 5204           nlm«ˇo»
 5205           "#
 5206    ));
 5207
 5208    cx.update_editor(|editor, window, cx| {
 5209        editor.add_selection_below(&Default::default(), window, cx);
 5210    });
 5211
 5212    cx.assert_editor_state(indoc!(
 5213        r#"abc
 5214           def«ˇg»hi
 5215
 5216           jk
 5217           nlm«ˇo»
 5218           "#
 5219    ));
 5220
 5221    cx.update_editor(|editor, window, cx| {
 5222        editor.add_selection_above(&Default::default(), window, cx);
 5223    });
 5224
 5225    cx.assert_editor_state(indoc!(
 5226        r#"abc
 5227           def«ˇg»hi
 5228
 5229           jk
 5230           nlmo
 5231           "#
 5232    ));
 5233
 5234    cx.update_editor(|editor, window, cx| {
 5235        editor.add_selection_above(&Default::default(), window, cx);
 5236    });
 5237
 5238    cx.assert_editor_state(indoc!(
 5239        r#"abc
 5240           def«ˇg»hi
 5241
 5242           jk
 5243           nlmo
 5244           "#
 5245    ));
 5246
 5247    // Change selections again
 5248    cx.set_state(indoc!(
 5249        r#"a«bc
 5250           defgˇ»hi
 5251
 5252           jk
 5253           nlmo
 5254           "#
 5255    ));
 5256
 5257    cx.update_editor(|editor, window, cx| {
 5258        editor.add_selection_below(&Default::default(), window, cx);
 5259    });
 5260
 5261    cx.assert_editor_state(indoc!(
 5262        r#"a«bcˇ»
 5263           d«efgˇ»hi
 5264
 5265           j«kˇ»
 5266           nlmo
 5267           "#
 5268    ));
 5269
 5270    cx.update_editor(|editor, window, cx| {
 5271        editor.add_selection_below(&Default::default(), window, cx);
 5272    });
 5273    cx.assert_editor_state(indoc!(
 5274        r#"a«bcˇ»
 5275           d«efgˇ»hi
 5276
 5277           j«kˇ»
 5278           n«lmoˇ»
 5279           "#
 5280    ));
 5281    cx.update_editor(|editor, window, cx| {
 5282        editor.add_selection_above(&Default::default(), window, cx);
 5283    });
 5284
 5285    cx.assert_editor_state(indoc!(
 5286        r#"a«bcˇ»
 5287           d«efgˇ»hi
 5288
 5289           j«kˇ»
 5290           nlmo
 5291           "#
 5292    ));
 5293
 5294    // Change selections again
 5295    cx.set_state(indoc!(
 5296        r#"abc
 5297           d«ˇefghi
 5298
 5299           jk
 5300           nlm»o
 5301           "#
 5302    ));
 5303
 5304    cx.update_editor(|editor, window, cx| {
 5305        editor.add_selection_above(&Default::default(), window, cx);
 5306    });
 5307
 5308    cx.assert_editor_state(indoc!(
 5309        r#"a«ˇbc»
 5310           d«ˇef»ghi
 5311
 5312           j«ˇk»
 5313           n«ˇlm»o
 5314           "#
 5315    ));
 5316
 5317    cx.update_editor(|editor, window, cx| {
 5318        editor.add_selection_below(&Default::default(), window, cx);
 5319    });
 5320
 5321    cx.assert_editor_state(indoc!(
 5322        r#"abc
 5323           d«ˇef»ghi
 5324
 5325           j«ˇk»
 5326           n«ˇlm»o
 5327           "#
 5328    ));
 5329}
 5330
 5331#[gpui::test]
 5332async fn test_select_next(cx: &mut TestAppContext) {
 5333    init_test(cx, |_| {});
 5334
 5335    let mut cx = EditorTestContext::new(cx).await;
 5336    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5337
 5338    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5339        .unwrap();
 5340    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5341
 5342    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5343        .unwrap();
 5344    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5345
 5346    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5347    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5348
 5349    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5350    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5351
 5352    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5353        .unwrap();
 5354    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5355
 5356    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5357        .unwrap();
 5358    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5359}
 5360
 5361#[gpui::test]
 5362async fn test_select_all_matches(cx: &mut TestAppContext) {
 5363    init_test(cx, |_| {});
 5364
 5365    let mut cx = EditorTestContext::new(cx).await;
 5366
 5367    // Test caret-only selections
 5368    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5369    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5370        .unwrap();
 5371    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5372
 5373    // Test left-to-right selections
 5374    cx.set_state("abc\n«abcˇ»\nabc");
 5375    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5376        .unwrap();
 5377    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5378
 5379    // Test right-to-left selections
 5380    cx.set_state("abc\n«ˇabc»\nabc");
 5381    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5382        .unwrap();
 5383    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5384
 5385    // Test selecting whitespace with caret selection
 5386    cx.set_state("abc\nˇ   abc\nabc");
 5387    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5388        .unwrap();
 5389    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5390
 5391    // Test selecting whitespace with left-to-right selection
 5392    cx.set_state("abc\n«ˇ  »abc\nabc");
 5393    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5394        .unwrap();
 5395    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5396
 5397    // Test no matches with right-to-left selection
 5398    cx.set_state("abc\n«  ˇ»abc\nabc");
 5399    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5400        .unwrap();
 5401    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5402}
 5403
 5404#[gpui::test]
 5405async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5406    init_test(cx, |_| {});
 5407
 5408    let mut cx = EditorTestContext::new(cx).await;
 5409    cx.set_state(
 5410        r#"let foo = 2;
 5411lˇet foo = 2;
 5412let fooˇ = 2;
 5413let foo = 2;
 5414let foo = ˇ2;"#,
 5415    );
 5416
 5417    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5418        .unwrap();
 5419    cx.assert_editor_state(
 5420        r#"let foo = 2;
 5421«letˇ» foo = 2;
 5422let «fooˇ» = 2;
 5423let foo = 2;
 5424let foo = «2ˇ»;"#,
 5425    );
 5426
 5427    // noop for multiple selections with different contents
 5428    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5429        .unwrap();
 5430    cx.assert_editor_state(
 5431        r#"let foo = 2;
 5432«letˇ» foo = 2;
 5433let «fooˇ» = 2;
 5434let foo = 2;
 5435let foo = «2ˇ»;"#,
 5436    );
 5437}
 5438
 5439#[gpui::test]
 5440async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5441    init_test(cx, |_| {});
 5442
 5443    let mut cx =
 5444        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5445
 5446    cx.assert_editor_state(indoc! {"
 5447        ˇbbb
 5448        ccc
 5449
 5450        bbb
 5451        ccc
 5452        "});
 5453    cx.dispatch_action(SelectPrevious::default());
 5454    cx.assert_editor_state(indoc! {"
 5455                «bbbˇ»
 5456                ccc
 5457
 5458                bbb
 5459                ccc
 5460                "});
 5461    cx.dispatch_action(SelectPrevious::default());
 5462    cx.assert_editor_state(indoc! {"
 5463                «bbbˇ»
 5464                ccc
 5465
 5466                «bbbˇ»
 5467                ccc
 5468                "});
 5469}
 5470
 5471#[gpui::test]
 5472async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5473    init_test(cx, |_| {});
 5474
 5475    let mut cx = EditorTestContext::new(cx).await;
 5476    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5477
 5478    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5479        .unwrap();
 5480    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5481
 5482    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5483        .unwrap();
 5484    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5485
 5486    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5487    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5488
 5489    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5490    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5491
 5492    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5493        .unwrap();
 5494    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5495
 5496    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5497        .unwrap();
 5498    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5499
 5500    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5501        .unwrap();
 5502    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5503}
 5504
 5505#[gpui::test]
 5506async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5507    init_test(cx, |_| {});
 5508
 5509    let mut cx = EditorTestContext::new(cx).await;
 5510    cx.set_state("");
 5511
 5512    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5513        .unwrap();
 5514    cx.assert_editor_state("«aˇ»");
 5515    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5516        .unwrap();
 5517    cx.assert_editor_state("«aˇ»");
 5518}
 5519
 5520#[gpui::test]
 5521async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5522    init_test(cx, |_| {});
 5523
 5524    let mut cx = EditorTestContext::new(cx).await;
 5525    cx.set_state(
 5526        r#"let foo = 2;
 5527lˇet foo = 2;
 5528let fooˇ = 2;
 5529let foo = 2;
 5530let foo = ˇ2;"#,
 5531    );
 5532
 5533    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5534        .unwrap();
 5535    cx.assert_editor_state(
 5536        r#"let foo = 2;
 5537«letˇ» foo = 2;
 5538let «fooˇ» = 2;
 5539let foo = 2;
 5540let foo = «2ˇ»;"#,
 5541    );
 5542
 5543    // noop for multiple selections with different contents
 5544    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5545        .unwrap();
 5546    cx.assert_editor_state(
 5547        r#"let foo = 2;
 5548«letˇ» foo = 2;
 5549let «fooˇ» = 2;
 5550let foo = 2;
 5551let foo = «2ˇ»;"#,
 5552    );
 5553}
 5554
 5555#[gpui::test]
 5556async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5557    init_test(cx, |_| {});
 5558
 5559    let mut cx = EditorTestContext::new(cx).await;
 5560    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5561
 5562    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5563        .unwrap();
 5564    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5565
 5566    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5567        .unwrap();
 5568    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5569
 5570    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5571    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5572
 5573    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5574    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5575
 5576    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5577        .unwrap();
 5578    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5579
 5580    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5581        .unwrap();
 5582    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5583}
 5584
 5585#[gpui::test]
 5586async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5587    init_test(cx, |_| {});
 5588
 5589    let language = Arc::new(Language::new(
 5590        LanguageConfig::default(),
 5591        Some(tree_sitter_rust::LANGUAGE.into()),
 5592    ));
 5593
 5594    let text = r#"
 5595        use mod1::mod2::{mod3, mod4};
 5596
 5597        fn fn_1(param1: bool, param2: &str) {
 5598            let var1 = "text";
 5599        }
 5600    "#
 5601    .unindent();
 5602
 5603    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5604    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5605    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5606
 5607    editor
 5608        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5609        .await;
 5610
 5611    editor.update_in(cx, |editor, window, cx| {
 5612        editor.change_selections(None, window, cx, |s| {
 5613            s.select_display_ranges([
 5614                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5615                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5616                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5617            ]);
 5618        });
 5619        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5620    });
 5621    editor.update(cx, |editor, cx| {
 5622        assert_text_with_selections(
 5623            editor,
 5624            indoc! {r#"
 5625                use mod1::mod2::{mod3, «mod4ˇ»};
 5626
 5627                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5628                    let var1 = "«textˇ»";
 5629                }
 5630            "#},
 5631            cx,
 5632        );
 5633    });
 5634
 5635    editor.update_in(cx, |editor, window, cx| {
 5636        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5637    });
 5638    editor.update(cx, |editor, cx| {
 5639        assert_text_with_selections(
 5640            editor,
 5641            indoc! {r#"
 5642                use mod1::mod2::«{mod3, mod4}ˇ»;
 5643
 5644                «ˇfn fn_1(param1: bool, param2: &str) {
 5645                    let var1 = "text";
 5646 5647            "#},
 5648            cx,
 5649        );
 5650    });
 5651
 5652    editor.update_in(cx, |editor, window, cx| {
 5653        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5654    });
 5655    assert_eq!(
 5656        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5657        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5658    );
 5659
 5660    // Trying to expand the selected syntax node one more time has no effect.
 5661    editor.update_in(cx, |editor, window, cx| {
 5662        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5663    });
 5664    assert_eq!(
 5665        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5666        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5667    );
 5668
 5669    editor.update_in(cx, |editor, window, cx| {
 5670        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5671    });
 5672    editor.update(cx, |editor, cx| {
 5673        assert_text_with_selections(
 5674            editor,
 5675            indoc! {r#"
 5676                use mod1::mod2::«{mod3, mod4}ˇ»;
 5677
 5678                «ˇfn fn_1(param1: bool, param2: &str) {
 5679                    let var1 = "text";
 5680 5681            "#},
 5682            cx,
 5683        );
 5684    });
 5685
 5686    editor.update_in(cx, |editor, window, cx| {
 5687        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5688    });
 5689    editor.update(cx, |editor, cx| {
 5690        assert_text_with_selections(
 5691            editor,
 5692            indoc! {r#"
 5693                use mod1::mod2::{mod3, «mod4ˇ»};
 5694
 5695                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5696                    let var1 = "«textˇ»";
 5697                }
 5698            "#},
 5699            cx,
 5700        );
 5701    });
 5702
 5703    editor.update_in(cx, |editor, window, cx| {
 5704        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5705    });
 5706    editor.update(cx, |editor, cx| {
 5707        assert_text_with_selections(
 5708            editor,
 5709            indoc! {r#"
 5710                use mod1::mod2::{mod3, mo«ˇ»d4};
 5711
 5712                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5713                    let var1 = "te«ˇ»xt";
 5714                }
 5715            "#},
 5716            cx,
 5717        );
 5718    });
 5719
 5720    // Trying to shrink the selected syntax node one more time has no effect.
 5721    editor.update_in(cx, |editor, window, cx| {
 5722        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5723    });
 5724    editor.update_in(cx, |editor, _, cx| {
 5725        assert_text_with_selections(
 5726            editor,
 5727            indoc! {r#"
 5728                use mod1::mod2::{mod3, mo«ˇ»d4};
 5729
 5730                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5731                    let var1 = "te«ˇ»xt";
 5732                }
 5733            "#},
 5734            cx,
 5735        );
 5736    });
 5737
 5738    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5739    // a fold.
 5740    editor.update_in(cx, |editor, window, cx| {
 5741        editor.fold_creases(
 5742            vec![
 5743                Crease::simple(
 5744                    Point::new(0, 21)..Point::new(0, 24),
 5745                    FoldPlaceholder::test(),
 5746                ),
 5747                Crease::simple(
 5748                    Point::new(3, 20)..Point::new(3, 22),
 5749                    FoldPlaceholder::test(),
 5750                ),
 5751            ],
 5752            true,
 5753            window,
 5754            cx,
 5755        );
 5756        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5757    });
 5758    editor.update(cx, |editor, cx| {
 5759        assert_text_with_selections(
 5760            editor,
 5761            indoc! {r#"
 5762                use mod1::mod2::«{mod3, mod4}ˇ»;
 5763
 5764                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5765                    «let var1 = "text";ˇ»
 5766                }
 5767            "#},
 5768            cx,
 5769        );
 5770    });
 5771}
 5772
 5773#[gpui::test]
 5774async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5775    init_test(cx, |_| {});
 5776
 5777    let base_text = r#"
 5778        impl A {
 5779            // this is an uncommitted comment
 5780
 5781            fn b() {
 5782                c();
 5783            }
 5784
 5785            // this is another uncommitted comment
 5786
 5787            fn d() {
 5788                // e
 5789                // f
 5790            }
 5791        }
 5792
 5793        fn g() {
 5794            // h
 5795        }
 5796    "#
 5797    .unindent();
 5798
 5799    let text = r#"
 5800        ˇimpl A {
 5801
 5802            fn b() {
 5803                c();
 5804            }
 5805
 5806            fn d() {
 5807                // e
 5808                // f
 5809            }
 5810        }
 5811
 5812        fn g() {
 5813            // h
 5814        }
 5815    "#
 5816    .unindent();
 5817
 5818    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5819    cx.set_state(&text);
 5820    cx.set_head_text(&base_text);
 5821    cx.update_editor(|editor, window, cx| {
 5822        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5823    });
 5824
 5825    cx.assert_state_with_diff(
 5826        "
 5827        ˇimpl A {
 5828      -     // this is an uncommitted comment
 5829
 5830            fn b() {
 5831                c();
 5832            }
 5833
 5834      -     // this is another uncommitted comment
 5835      -
 5836            fn d() {
 5837                // e
 5838                // f
 5839            }
 5840        }
 5841
 5842        fn g() {
 5843            // h
 5844        }
 5845    "
 5846        .unindent(),
 5847    );
 5848
 5849    let expected_display_text = "
 5850        impl A {
 5851            // this is an uncommitted comment
 5852
 5853            fn b() {
 5854 5855            }
 5856
 5857            // this is another uncommitted comment
 5858
 5859            fn d() {
 5860 5861            }
 5862        }
 5863
 5864        fn g() {
 5865 5866        }
 5867        "
 5868    .unindent();
 5869
 5870    cx.update_editor(|editor, window, cx| {
 5871        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5872        assert_eq!(editor.display_text(cx), expected_display_text);
 5873    });
 5874}
 5875
 5876#[gpui::test]
 5877async fn test_autoindent(cx: &mut TestAppContext) {
 5878    init_test(cx, |_| {});
 5879
 5880    let language = Arc::new(
 5881        Language::new(
 5882            LanguageConfig {
 5883                brackets: BracketPairConfig {
 5884                    pairs: vec![
 5885                        BracketPair {
 5886                            start: "{".to_string(),
 5887                            end: "}".to_string(),
 5888                            close: false,
 5889                            surround: false,
 5890                            newline: true,
 5891                        },
 5892                        BracketPair {
 5893                            start: "(".to_string(),
 5894                            end: ")".to_string(),
 5895                            close: false,
 5896                            surround: false,
 5897                            newline: true,
 5898                        },
 5899                    ],
 5900                    ..Default::default()
 5901                },
 5902                ..Default::default()
 5903            },
 5904            Some(tree_sitter_rust::LANGUAGE.into()),
 5905        )
 5906        .with_indents_query(
 5907            r#"
 5908                (_ "(" ")" @end) @indent
 5909                (_ "{" "}" @end) @indent
 5910            "#,
 5911        )
 5912        .unwrap(),
 5913    );
 5914
 5915    let text = "fn a() {}";
 5916
 5917    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5918    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5919    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5920    editor
 5921        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5922        .await;
 5923
 5924    editor.update_in(cx, |editor, window, cx| {
 5925        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5926        editor.newline(&Newline, window, cx);
 5927        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5928        assert_eq!(
 5929            editor.selections.ranges(cx),
 5930            &[
 5931                Point::new(1, 4)..Point::new(1, 4),
 5932                Point::new(3, 4)..Point::new(3, 4),
 5933                Point::new(5, 0)..Point::new(5, 0)
 5934            ]
 5935        );
 5936    });
 5937}
 5938
 5939#[gpui::test]
 5940async fn test_autoindent_selections(cx: &mut TestAppContext) {
 5941    init_test(cx, |_| {});
 5942
 5943    {
 5944        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5945        cx.set_state(indoc! {"
 5946            impl A {
 5947
 5948                fn b() {}
 5949
 5950            «fn c() {
 5951
 5952            }ˇ»
 5953            }
 5954        "});
 5955
 5956        cx.update_editor(|editor, window, cx| {
 5957            editor.autoindent(&Default::default(), window, cx);
 5958        });
 5959
 5960        cx.assert_editor_state(indoc! {"
 5961            impl A {
 5962
 5963                fn b() {}
 5964
 5965                «fn c() {
 5966
 5967                }ˇ»
 5968            }
 5969        "});
 5970    }
 5971
 5972    {
 5973        let mut cx = EditorTestContext::new_multibuffer(
 5974            cx,
 5975            [indoc! { "
 5976                impl A {
 5977                «
 5978                // a
 5979                fn b(){}
 5980                »
 5981                «
 5982                    }
 5983                    fn c(){}
 5984                »
 5985            "}],
 5986        );
 5987
 5988        let buffer = cx.update_editor(|editor, _, cx| {
 5989            let buffer = editor.buffer().update(cx, |buffer, _| {
 5990                buffer.all_buffers().iter().next().unwrap().clone()
 5991            });
 5992            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5993            buffer
 5994        });
 5995
 5996        cx.run_until_parked();
 5997        cx.update_editor(|editor, window, cx| {
 5998            editor.select_all(&Default::default(), window, cx);
 5999            editor.autoindent(&Default::default(), window, cx)
 6000        });
 6001        cx.run_until_parked();
 6002
 6003        cx.update(|_, cx| {
 6004            pretty_assertions::assert_eq!(
 6005                buffer.read(cx).text(),
 6006                indoc! { "
 6007                    impl A {
 6008
 6009                        // a
 6010                        fn b(){}
 6011
 6012
 6013                    }
 6014                    fn c(){}
 6015
 6016                " }
 6017            )
 6018        });
 6019    }
 6020}
 6021
 6022#[gpui::test]
 6023async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6024    init_test(cx, |_| {});
 6025
 6026    let mut cx = EditorTestContext::new(cx).await;
 6027
 6028    let language = Arc::new(Language::new(
 6029        LanguageConfig {
 6030            brackets: BracketPairConfig {
 6031                pairs: vec![
 6032                    BracketPair {
 6033                        start: "{".to_string(),
 6034                        end: "}".to_string(),
 6035                        close: true,
 6036                        surround: true,
 6037                        newline: true,
 6038                    },
 6039                    BracketPair {
 6040                        start: "(".to_string(),
 6041                        end: ")".to_string(),
 6042                        close: true,
 6043                        surround: true,
 6044                        newline: true,
 6045                    },
 6046                    BracketPair {
 6047                        start: "/*".to_string(),
 6048                        end: " */".to_string(),
 6049                        close: true,
 6050                        surround: true,
 6051                        newline: true,
 6052                    },
 6053                    BracketPair {
 6054                        start: "[".to_string(),
 6055                        end: "]".to_string(),
 6056                        close: false,
 6057                        surround: false,
 6058                        newline: true,
 6059                    },
 6060                    BracketPair {
 6061                        start: "\"".to_string(),
 6062                        end: "\"".to_string(),
 6063                        close: true,
 6064                        surround: true,
 6065                        newline: false,
 6066                    },
 6067                    BracketPair {
 6068                        start: "<".to_string(),
 6069                        end: ">".to_string(),
 6070                        close: false,
 6071                        surround: true,
 6072                        newline: true,
 6073                    },
 6074                ],
 6075                ..Default::default()
 6076            },
 6077            autoclose_before: "})]".to_string(),
 6078            ..Default::default()
 6079        },
 6080        Some(tree_sitter_rust::LANGUAGE.into()),
 6081    ));
 6082
 6083    cx.language_registry().add(language.clone());
 6084    cx.update_buffer(|buffer, cx| {
 6085        buffer.set_language(Some(language), cx);
 6086    });
 6087
 6088    cx.set_state(
 6089        &r#"
 6090            🏀ˇ
 6091            εˇ
 6092            ❤️ˇ
 6093        "#
 6094        .unindent(),
 6095    );
 6096
 6097    // autoclose multiple nested brackets at multiple cursors
 6098    cx.update_editor(|editor, window, cx| {
 6099        editor.handle_input("{", window, cx);
 6100        editor.handle_input("{", window, cx);
 6101        editor.handle_input("{", window, cx);
 6102    });
 6103    cx.assert_editor_state(
 6104        &"
 6105            🏀{{{ˇ}}}
 6106            ε{{{ˇ}}}
 6107            ❤️{{{ˇ}}}
 6108        "
 6109        .unindent(),
 6110    );
 6111
 6112    // insert a different closing bracket
 6113    cx.update_editor(|editor, window, cx| {
 6114        editor.handle_input(")", window, cx);
 6115    });
 6116    cx.assert_editor_state(
 6117        &"
 6118            🏀{{{)ˇ}}}
 6119            ε{{{)ˇ}}}
 6120            ❤️{{{)ˇ}}}
 6121        "
 6122        .unindent(),
 6123    );
 6124
 6125    // skip over the auto-closed brackets when typing a closing bracket
 6126    cx.update_editor(|editor, window, cx| {
 6127        editor.move_right(&MoveRight, window, cx);
 6128        editor.handle_input("}", window, cx);
 6129        editor.handle_input("}", window, cx);
 6130        editor.handle_input("}", window, cx);
 6131    });
 6132    cx.assert_editor_state(
 6133        &"
 6134            🏀{{{)}}}}ˇ
 6135            ε{{{)}}}}ˇ
 6136            ❤️{{{)}}}}ˇ
 6137        "
 6138        .unindent(),
 6139    );
 6140
 6141    // autoclose multi-character pairs
 6142    cx.set_state(
 6143        &"
 6144            ˇ
 6145            ˇ
 6146        "
 6147        .unindent(),
 6148    );
 6149    cx.update_editor(|editor, window, cx| {
 6150        editor.handle_input("/", window, cx);
 6151        editor.handle_input("*", window, cx);
 6152    });
 6153    cx.assert_editor_state(
 6154        &"
 6155            /*ˇ */
 6156            /*ˇ */
 6157        "
 6158        .unindent(),
 6159    );
 6160
 6161    // one cursor autocloses a multi-character pair, one cursor
 6162    // does not autoclose.
 6163    cx.set_state(
 6164        &"
 6165 6166            ˇ
 6167        "
 6168        .unindent(),
 6169    );
 6170    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6171    cx.assert_editor_state(
 6172        &"
 6173            /*ˇ */
 6174 6175        "
 6176        .unindent(),
 6177    );
 6178
 6179    // Don't autoclose if the next character isn't whitespace and isn't
 6180    // listed in the language's "autoclose_before" section.
 6181    cx.set_state("ˇa b");
 6182    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6183    cx.assert_editor_state("{ˇa b");
 6184
 6185    // Don't autoclose if `close` is false for the bracket pair
 6186    cx.set_state("ˇ");
 6187    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6188    cx.assert_editor_state("");
 6189
 6190    // Surround with brackets if text is selected
 6191    cx.set_state("«aˇ» b");
 6192    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6193    cx.assert_editor_state("{«aˇ»} b");
 6194
 6195    // Autclose pair where the start and end characters are the same
 6196    cx.set_state("");
 6197    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6198    cx.assert_editor_state("a\"ˇ\"");
 6199    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6200    cx.assert_editor_state("a\"\"ˇ");
 6201
 6202    // Don't autoclose pair if autoclose is disabled
 6203    cx.set_state("ˇ");
 6204    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6205    cx.assert_editor_state("");
 6206
 6207    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6208    cx.set_state("«aˇ» b");
 6209    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6210    cx.assert_editor_state("<«aˇ»> b");
 6211}
 6212
 6213#[gpui::test]
 6214async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6215    init_test(cx, |settings| {
 6216        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6217    });
 6218
 6219    let mut cx = EditorTestContext::new(cx).await;
 6220
 6221    let language = Arc::new(Language::new(
 6222        LanguageConfig {
 6223            brackets: BracketPairConfig {
 6224                pairs: vec![
 6225                    BracketPair {
 6226                        start: "{".to_string(),
 6227                        end: "}".to_string(),
 6228                        close: true,
 6229                        surround: true,
 6230                        newline: true,
 6231                    },
 6232                    BracketPair {
 6233                        start: "(".to_string(),
 6234                        end: ")".to_string(),
 6235                        close: true,
 6236                        surround: true,
 6237                        newline: true,
 6238                    },
 6239                    BracketPair {
 6240                        start: "[".to_string(),
 6241                        end: "]".to_string(),
 6242                        close: false,
 6243                        surround: false,
 6244                        newline: true,
 6245                    },
 6246                ],
 6247                ..Default::default()
 6248            },
 6249            autoclose_before: "})]".to_string(),
 6250            ..Default::default()
 6251        },
 6252        Some(tree_sitter_rust::LANGUAGE.into()),
 6253    ));
 6254
 6255    cx.language_registry().add(language.clone());
 6256    cx.update_buffer(|buffer, cx| {
 6257        buffer.set_language(Some(language), cx);
 6258    });
 6259
 6260    cx.set_state(
 6261        &"
 6262            ˇ
 6263            ˇ
 6264            ˇ
 6265        "
 6266        .unindent(),
 6267    );
 6268
 6269    // ensure only matching closing brackets are skipped over
 6270    cx.update_editor(|editor, window, cx| {
 6271        editor.handle_input("}", window, cx);
 6272        editor.move_left(&MoveLeft, window, cx);
 6273        editor.handle_input(")", window, cx);
 6274        editor.move_left(&MoveLeft, window, cx);
 6275    });
 6276    cx.assert_editor_state(
 6277        &"
 6278            ˇ)}
 6279            ˇ)}
 6280            ˇ)}
 6281        "
 6282        .unindent(),
 6283    );
 6284
 6285    // skip-over closing brackets at multiple cursors
 6286    cx.update_editor(|editor, window, cx| {
 6287        editor.handle_input(")", window, cx);
 6288        editor.handle_input("}", window, cx);
 6289    });
 6290    cx.assert_editor_state(
 6291        &"
 6292            )}ˇ
 6293            )}ˇ
 6294            )}ˇ
 6295        "
 6296        .unindent(),
 6297    );
 6298
 6299    // ignore non-close brackets
 6300    cx.update_editor(|editor, window, cx| {
 6301        editor.handle_input("]", window, cx);
 6302        editor.move_left(&MoveLeft, window, cx);
 6303        editor.handle_input("]", window, cx);
 6304    });
 6305    cx.assert_editor_state(
 6306        &"
 6307            )}]ˇ]
 6308            )}]ˇ]
 6309            )}]ˇ]
 6310        "
 6311        .unindent(),
 6312    );
 6313}
 6314
 6315#[gpui::test]
 6316async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6317    init_test(cx, |_| {});
 6318
 6319    let mut cx = EditorTestContext::new(cx).await;
 6320
 6321    let html_language = Arc::new(
 6322        Language::new(
 6323            LanguageConfig {
 6324                name: "HTML".into(),
 6325                brackets: BracketPairConfig {
 6326                    pairs: vec![
 6327                        BracketPair {
 6328                            start: "<".into(),
 6329                            end: ">".into(),
 6330                            close: true,
 6331                            ..Default::default()
 6332                        },
 6333                        BracketPair {
 6334                            start: "{".into(),
 6335                            end: "}".into(),
 6336                            close: true,
 6337                            ..Default::default()
 6338                        },
 6339                        BracketPair {
 6340                            start: "(".into(),
 6341                            end: ")".into(),
 6342                            close: true,
 6343                            ..Default::default()
 6344                        },
 6345                    ],
 6346                    ..Default::default()
 6347                },
 6348                autoclose_before: "})]>".into(),
 6349                ..Default::default()
 6350            },
 6351            Some(tree_sitter_html::LANGUAGE.into()),
 6352        )
 6353        .with_injection_query(
 6354            r#"
 6355            (script_element
 6356                (raw_text) @injection.content
 6357                (#set! injection.language "javascript"))
 6358            "#,
 6359        )
 6360        .unwrap(),
 6361    );
 6362
 6363    let javascript_language = Arc::new(Language::new(
 6364        LanguageConfig {
 6365            name: "JavaScript".into(),
 6366            brackets: BracketPairConfig {
 6367                pairs: vec![
 6368                    BracketPair {
 6369                        start: "/*".into(),
 6370                        end: " */".into(),
 6371                        close: true,
 6372                        ..Default::default()
 6373                    },
 6374                    BracketPair {
 6375                        start: "{".into(),
 6376                        end: "}".into(),
 6377                        close: true,
 6378                        ..Default::default()
 6379                    },
 6380                    BracketPair {
 6381                        start: "(".into(),
 6382                        end: ")".into(),
 6383                        close: true,
 6384                        ..Default::default()
 6385                    },
 6386                ],
 6387                ..Default::default()
 6388            },
 6389            autoclose_before: "})]>".into(),
 6390            ..Default::default()
 6391        },
 6392        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6393    ));
 6394
 6395    cx.language_registry().add(html_language.clone());
 6396    cx.language_registry().add(javascript_language.clone());
 6397
 6398    cx.update_buffer(|buffer, cx| {
 6399        buffer.set_language(Some(html_language), cx);
 6400    });
 6401
 6402    cx.set_state(
 6403        &r#"
 6404            <body>ˇ
 6405                <script>
 6406                    var x = 1;ˇ
 6407                </script>
 6408            </body>ˇ
 6409        "#
 6410        .unindent(),
 6411    );
 6412
 6413    // Precondition: different languages are active at different locations.
 6414    cx.update_editor(|editor, window, cx| {
 6415        let snapshot = editor.snapshot(window, cx);
 6416        let cursors = editor.selections.ranges::<usize>(cx);
 6417        let languages = cursors
 6418            .iter()
 6419            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6420            .collect::<Vec<_>>();
 6421        assert_eq!(
 6422            languages,
 6423            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6424        );
 6425    });
 6426
 6427    // Angle brackets autoclose in HTML, but not JavaScript.
 6428    cx.update_editor(|editor, window, cx| {
 6429        editor.handle_input("<", window, cx);
 6430        editor.handle_input("a", window, cx);
 6431    });
 6432    cx.assert_editor_state(
 6433        &r#"
 6434            <body><aˇ>
 6435                <script>
 6436                    var x = 1;<aˇ
 6437                </script>
 6438            </body><aˇ>
 6439        "#
 6440        .unindent(),
 6441    );
 6442
 6443    // Curly braces and parens autoclose in both HTML and JavaScript.
 6444    cx.update_editor(|editor, window, cx| {
 6445        editor.handle_input(" b=", window, cx);
 6446        editor.handle_input("{", window, cx);
 6447        editor.handle_input("c", window, cx);
 6448        editor.handle_input("(", window, cx);
 6449    });
 6450    cx.assert_editor_state(
 6451        &r#"
 6452            <body><a b={c(ˇ)}>
 6453                <script>
 6454                    var x = 1;<a b={c(ˇ)}
 6455                </script>
 6456            </body><a b={c(ˇ)}>
 6457        "#
 6458        .unindent(),
 6459    );
 6460
 6461    // Brackets that were already autoclosed are skipped.
 6462    cx.update_editor(|editor, window, cx| {
 6463        editor.handle_input(")", window, cx);
 6464        editor.handle_input("d", window, cx);
 6465        editor.handle_input("}", window, cx);
 6466    });
 6467    cx.assert_editor_state(
 6468        &r#"
 6469            <body><a b={c()d}ˇ>
 6470                <script>
 6471                    var x = 1;<a b={c()d}ˇ
 6472                </script>
 6473            </body><a b={c()d}ˇ>
 6474        "#
 6475        .unindent(),
 6476    );
 6477    cx.update_editor(|editor, window, cx| {
 6478        editor.handle_input(">", window, cx);
 6479    });
 6480    cx.assert_editor_state(
 6481        &r#"
 6482            <body><a b={c()d}>ˇ
 6483                <script>
 6484                    var x = 1;<a b={c()d}>ˇ
 6485                </script>
 6486            </body><a b={c()d}>ˇ
 6487        "#
 6488        .unindent(),
 6489    );
 6490
 6491    // Reset
 6492    cx.set_state(
 6493        &r#"
 6494            <body>ˇ
 6495                <script>
 6496                    var x = 1;ˇ
 6497                </script>
 6498            </body>ˇ
 6499        "#
 6500        .unindent(),
 6501    );
 6502
 6503    cx.update_editor(|editor, window, cx| {
 6504        editor.handle_input("<", window, cx);
 6505    });
 6506    cx.assert_editor_state(
 6507        &r#"
 6508            <body><ˇ>
 6509                <script>
 6510                    var x = 1;<ˇ
 6511                </script>
 6512            </body><ˇ>
 6513        "#
 6514        .unindent(),
 6515    );
 6516
 6517    // When backspacing, the closing angle brackets are removed.
 6518    cx.update_editor(|editor, window, cx| {
 6519        editor.backspace(&Backspace, window, cx);
 6520    });
 6521    cx.assert_editor_state(
 6522        &r#"
 6523            <body>ˇ
 6524                <script>
 6525                    var x = 1;ˇ
 6526                </script>
 6527            </body>ˇ
 6528        "#
 6529        .unindent(),
 6530    );
 6531
 6532    // Block comments autoclose in JavaScript, but not HTML.
 6533    cx.update_editor(|editor, window, cx| {
 6534        editor.handle_input("/", window, cx);
 6535        editor.handle_input("*", window, cx);
 6536    });
 6537    cx.assert_editor_state(
 6538        &r#"
 6539            <body>/*ˇ
 6540                <script>
 6541                    var x = 1;/*ˇ */
 6542                </script>
 6543            </body>/*ˇ
 6544        "#
 6545        .unindent(),
 6546    );
 6547}
 6548
 6549#[gpui::test]
 6550async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6551    init_test(cx, |_| {});
 6552
 6553    let mut cx = EditorTestContext::new(cx).await;
 6554
 6555    let rust_language = Arc::new(
 6556        Language::new(
 6557            LanguageConfig {
 6558                name: "Rust".into(),
 6559                brackets: serde_json::from_value(json!([
 6560                    { "start": "{", "end": "}", "close": true, "newline": true },
 6561                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6562                ]))
 6563                .unwrap(),
 6564                autoclose_before: "})]>".into(),
 6565                ..Default::default()
 6566            },
 6567            Some(tree_sitter_rust::LANGUAGE.into()),
 6568        )
 6569        .with_override_query("(string_literal) @string")
 6570        .unwrap(),
 6571    );
 6572
 6573    cx.language_registry().add(rust_language.clone());
 6574    cx.update_buffer(|buffer, cx| {
 6575        buffer.set_language(Some(rust_language), cx);
 6576    });
 6577
 6578    cx.set_state(
 6579        &r#"
 6580            let x = ˇ
 6581        "#
 6582        .unindent(),
 6583    );
 6584
 6585    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6586    cx.update_editor(|editor, window, cx| {
 6587        editor.handle_input("\"", window, cx);
 6588    });
 6589    cx.assert_editor_state(
 6590        &r#"
 6591            let x = "ˇ"
 6592        "#
 6593        .unindent(),
 6594    );
 6595
 6596    // Inserting another quotation mark. The cursor moves across the existing
 6597    // automatically-inserted quotation mark.
 6598    cx.update_editor(|editor, window, cx| {
 6599        editor.handle_input("\"", window, cx);
 6600    });
 6601    cx.assert_editor_state(
 6602        &r#"
 6603            let x = ""ˇ
 6604        "#
 6605        .unindent(),
 6606    );
 6607
 6608    // Reset
 6609    cx.set_state(
 6610        &r#"
 6611            let x = ˇ
 6612        "#
 6613        .unindent(),
 6614    );
 6615
 6616    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6617    cx.update_editor(|editor, window, cx| {
 6618        editor.handle_input("\"", window, cx);
 6619        editor.handle_input(" ", window, cx);
 6620        editor.move_left(&Default::default(), window, cx);
 6621        editor.handle_input("\\", window, cx);
 6622        editor.handle_input("\"", window, cx);
 6623    });
 6624    cx.assert_editor_state(
 6625        &r#"
 6626            let x = "\"ˇ "
 6627        "#
 6628        .unindent(),
 6629    );
 6630
 6631    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6632    // mark. Nothing is inserted.
 6633    cx.update_editor(|editor, window, cx| {
 6634        editor.move_right(&Default::default(), window, cx);
 6635        editor.handle_input("\"", window, cx);
 6636    });
 6637    cx.assert_editor_state(
 6638        &r#"
 6639            let x = "\" "ˇ
 6640        "#
 6641        .unindent(),
 6642    );
 6643}
 6644
 6645#[gpui::test]
 6646async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6647    init_test(cx, |_| {});
 6648
 6649    let language = Arc::new(Language::new(
 6650        LanguageConfig {
 6651            brackets: BracketPairConfig {
 6652                pairs: vec![
 6653                    BracketPair {
 6654                        start: "{".to_string(),
 6655                        end: "}".to_string(),
 6656                        close: true,
 6657                        surround: true,
 6658                        newline: true,
 6659                    },
 6660                    BracketPair {
 6661                        start: "/* ".to_string(),
 6662                        end: "*/".to_string(),
 6663                        close: true,
 6664                        surround: true,
 6665                        ..Default::default()
 6666                    },
 6667                ],
 6668                ..Default::default()
 6669            },
 6670            ..Default::default()
 6671        },
 6672        Some(tree_sitter_rust::LANGUAGE.into()),
 6673    ));
 6674
 6675    let text = r#"
 6676        a
 6677        b
 6678        c
 6679    "#
 6680    .unindent();
 6681
 6682    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6683    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6684    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6685    editor
 6686        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6687        .await;
 6688
 6689    editor.update_in(cx, |editor, window, cx| {
 6690        editor.change_selections(None, window, cx, |s| {
 6691            s.select_display_ranges([
 6692                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6693                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6694                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6695            ])
 6696        });
 6697
 6698        editor.handle_input("{", window, cx);
 6699        editor.handle_input("{", window, cx);
 6700        editor.handle_input("{", window, cx);
 6701        assert_eq!(
 6702            editor.text(cx),
 6703            "
 6704                {{{a}}}
 6705                {{{b}}}
 6706                {{{c}}}
 6707            "
 6708            .unindent()
 6709        );
 6710        assert_eq!(
 6711            editor.selections.display_ranges(cx),
 6712            [
 6713                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6714                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6715                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6716            ]
 6717        );
 6718
 6719        editor.undo(&Undo, window, cx);
 6720        editor.undo(&Undo, window, cx);
 6721        editor.undo(&Undo, window, cx);
 6722        assert_eq!(
 6723            editor.text(cx),
 6724            "
 6725                a
 6726                b
 6727                c
 6728            "
 6729            .unindent()
 6730        );
 6731        assert_eq!(
 6732            editor.selections.display_ranges(cx),
 6733            [
 6734                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6735                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6736                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6737            ]
 6738        );
 6739
 6740        // Ensure inserting the first character of a multi-byte bracket pair
 6741        // doesn't surround the selections with the bracket.
 6742        editor.handle_input("/", window, cx);
 6743        assert_eq!(
 6744            editor.text(cx),
 6745            "
 6746                /
 6747                /
 6748                /
 6749            "
 6750            .unindent()
 6751        );
 6752        assert_eq!(
 6753            editor.selections.display_ranges(cx),
 6754            [
 6755                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6756                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6757                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6758            ]
 6759        );
 6760
 6761        editor.undo(&Undo, window, cx);
 6762        assert_eq!(
 6763            editor.text(cx),
 6764            "
 6765                a
 6766                b
 6767                c
 6768            "
 6769            .unindent()
 6770        );
 6771        assert_eq!(
 6772            editor.selections.display_ranges(cx),
 6773            [
 6774                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6775                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6776                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6777            ]
 6778        );
 6779
 6780        // Ensure inserting the last character of a multi-byte bracket pair
 6781        // doesn't surround the selections with the bracket.
 6782        editor.handle_input("*", window, cx);
 6783        assert_eq!(
 6784            editor.text(cx),
 6785            "
 6786                *
 6787                *
 6788                *
 6789            "
 6790            .unindent()
 6791        );
 6792        assert_eq!(
 6793            editor.selections.display_ranges(cx),
 6794            [
 6795                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6796                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6797                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6798            ]
 6799        );
 6800    });
 6801}
 6802
 6803#[gpui::test]
 6804async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6805    init_test(cx, |_| {});
 6806
 6807    let language = Arc::new(Language::new(
 6808        LanguageConfig {
 6809            brackets: BracketPairConfig {
 6810                pairs: vec![BracketPair {
 6811                    start: "{".to_string(),
 6812                    end: "}".to_string(),
 6813                    close: true,
 6814                    surround: true,
 6815                    newline: true,
 6816                }],
 6817                ..Default::default()
 6818            },
 6819            autoclose_before: "}".to_string(),
 6820            ..Default::default()
 6821        },
 6822        Some(tree_sitter_rust::LANGUAGE.into()),
 6823    ));
 6824
 6825    let text = r#"
 6826        a
 6827        b
 6828        c
 6829    "#
 6830    .unindent();
 6831
 6832    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6833    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6834    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6835    editor
 6836        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6837        .await;
 6838
 6839    editor.update_in(cx, |editor, window, cx| {
 6840        editor.change_selections(None, window, cx, |s| {
 6841            s.select_ranges([
 6842                Point::new(0, 1)..Point::new(0, 1),
 6843                Point::new(1, 1)..Point::new(1, 1),
 6844                Point::new(2, 1)..Point::new(2, 1),
 6845            ])
 6846        });
 6847
 6848        editor.handle_input("{", window, cx);
 6849        editor.handle_input("{", window, cx);
 6850        editor.handle_input("_", window, cx);
 6851        assert_eq!(
 6852            editor.text(cx),
 6853            "
 6854                a{{_}}
 6855                b{{_}}
 6856                c{{_}}
 6857            "
 6858            .unindent()
 6859        );
 6860        assert_eq!(
 6861            editor.selections.ranges::<Point>(cx),
 6862            [
 6863                Point::new(0, 4)..Point::new(0, 4),
 6864                Point::new(1, 4)..Point::new(1, 4),
 6865                Point::new(2, 4)..Point::new(2, 4)
 6866            ]
 6867        );
 6868
 6869        editor.backspace(&Default::default(), window, cx);
 6870        editor.backspace(&Default::default(), window, cx);
 6871        assert_eq!(
 6872            editor.text(cx),
 6873            "
 6874                a{}
 6875                b{}
 6876                c{}
 6877            "
 6878            .unindent()
 6879        );
 6880        assert_eq!(
 6881            editor.selections.ranges::<Point>(cx),
 6882            [
 6883                Point::new(0, 2)..Point::new(0, 2),
 6884                Point::new(1, 2)..Point::new(1, 2),
 6885                Point::new(2, 2)..Point::new(2, 2)
 6886            ]
 6887        );
 6888
 6889        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6890        assert_eq!(
 6891            editor.text(cx),
 6892            "
 6893                a
 6894                b
 6895                c
 6896            "
 6897            .unindent()
 6898        );
 6899        assert_eq!(
 6900            editor.selections.ranges::<Point>(cx),
 6901            [
 6902                Point::new(0, 1)..Point::new(0, 1),
 6903                Point::new(1, 1)..Point::new(1, 1),
 6904                Point::new(2, 1)..Point::new(2, 1)
 6905            ]
 6906        );
 6907    });
 6908}
 6909
 6910#[gpui::test]
 6911async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 6912    init_test(cx, |settings| {
 6913        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6914    });
 6915
 6916    let mut cx = EditorTestContext::new(cx).await;
 6917
 6918    let language = Arc::new(Language::new(
 6919        LanguageConfig {
 6920            brackets: BracketPairConfig {
 6921                pairs: vec![
 6922                    BracketPair {
 6923                        start: "{".to_string(),
 6924                        end: "}".to_string(),
 6925                        close: true,
 6926                        surround: true,
 6927                        newline: true,
 6928                    },
 6929                    BracketPair {
 6930                        start: "(".to_string(),
 6931                        end: ")".to_string(),
 6932                        close: true,
 6933                        surround: true,
 6934                        newline: true,
 6935                    },
 6936                    BracketPair {
 6937                        start: "[".to_string(),
 6938                        end: "]".to_string(),
 6939                        close: false,
 6940                        surround: true,
 6941                        newline: true,
 6942                    },
 6943                ],
 6944                ..Default::default()
 6945            },
 6946            autoclose_before: "})]".to_string(),
 6947            ..Default::default()
 6948        },
 6949        Some(tree_sitter_rust::LANGUAGE.into()),
 6950    ));
 6951
 6952    cx.language_registry().add(language.clone());
 6953    cx.update_buffer(|buffer, cx| {
 6954        buffer.set_language(Some(language), cx);
 6955    });
 6956
 6957    cx.set_state(
 6958        &"
 6959            {(ˇ)}
 6960            [[ˇ]]
 6961            {(ˇ)}
 6962        "
 6963        .unindent(),
 6964    );
 6965
 6966    cx.update_editor(|editor, window, cx| {
 6967        editor.backspace(&Default::default(), window, cx);
 6968        editor.backspace(&Default::default(), window, cx);
 6969    });
 6970
 6971    cx.assert_editor_state(
 6972        &"
 6973            ˇ
 6974            ˇ]]
 6975            ˇ
 6976        "
 6977        .unindent(),
 6978    );
 6979
 6980    cx.update_editor(|editor, window, cx| {
 6981        editor.handle_input("{", window, cx);
 6982        editor.handle_input("{", window, cx);
 6983        editor.move_right(&MoveRight, window, cx);
 6984        editor.move_right(&MoveRight, window, cx);
 6985        editor.move_left(&MoveLeft, window, cx);
 6986        editor.move_left(&MoveLeft, window, cx);
 6987        editor.backspace(&Default::default(), window, cx);
 6988    });
 6989
 6990    cx.assert_editor_state(
 6991        &"
 6992            {ˇ}
 6993            {ˇ}]]
 6994            {ˇ}
 6995        "
 6996        .unindent(),
 6997    );
 6998
 6999    cx.update_editor(|editor, window, cx| {
 7000        editor.backspace(&Default::default(), window, cx);
 7001    });
 7002
 7003    cx.assert_editor_state(
 7004        &"
 7005            ˇ
 7006            ˇ]]
 7007            ˇ
 7008        "
 7009        .unindent(),
 7010    );
 7011}
 7012
 7013#[gpui::test]
 7014async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7015    init_test(cx, |_| {});
 7016
 7017    let language = Arc::new(Language::new(
 7018        LanguageConfig::default(),
 7019        Some(tree_sitter_rust::LANGUAGE.into()),
 7020    ));
 7021
 7022    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7023    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7024    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7025    editor
 7026        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7027        .await;
 7028
 7029    editor.update_in(cx, |editor, window, cx| {
 7030        editor.set_auto_replace_emoji_shortcode(true);
 7031
 7032        editor.handle_input("Hello ", window, cx);
 7033        editor.handle_input(":wave", window, cx);
 7034        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7035
 7036        editor.handle_input(":", window, cx);
 7037        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7038
 7039        editor.handle_input(" :smile", window, cx);
 7040        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7041
 7042        editor.handle_input(":", window, cx);
 7043        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7044
 7045        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7046        editor.handle_input(":wave", window, cx);
 7047        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7048
 7049        editor.handle_input(":", window, cx);
 7050        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7051
 7052        editor.handle_input(":1", window, cx);
 7053        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7054
 7055        editor.handle_input(":", window, cx);
 7056        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7057
 7058        // Ensure shortcode does not get replaced when it is part of a word
 7059        editor.handle_input(" Test:wave", window, cx);
 7060        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7061
 7062        editor.handle_input(":", window, cx);
 7063        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7064
 7065        editor.set_auto_replace_emoji_shortcode(false);
 7066
 7067        // Ensure shortcode does not get replaced when auto replace is off
 7068        editor.handle_input(" :wave", window, cx);
 7069        assert_eq!(
 7070            editor.text(cx),
 7071            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7072        );
 7073
 7074        editor.handle_input(":", window, cx);
 7075        assert_eq!(
 7076            editor.text(cx),
 7077            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7078        );
 7079    });
 7080}
 7081
 7082#[gpui::test]
 7083async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7084    init_test(cx, |_| {});
 7085
 7086    let (text, insertion_ranges) = marked_text_ranges(
 7087        indoc! {"
 7088            ˇ
 7089        "},
 7090        false,
 7091    );
 7092
 7093    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7094    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7095
 7096    _ = editor.update_in(cx, |editor, window, cx| {
 7097        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7098
 7099        editor
 7100            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7101            .unwrap();
 7102
 7103        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7104            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7105            assert_eq!(editor.text(cx), expected_text);
 7106            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7107        }
 7108
 7109        assert(
 7110            editor,
 7111            cx,
 7112            indoc! {"
 7113            type «» =•
 7114            "},
 7115        );
 7116
 7117        assert!(editor.context_menu_visible(), "There should be a matches");
 7118    });
 7119}
 7120
 7121#[gpui::test]
 7122async fn test_snippets(cx: &mut TestAppContext) {
 7123    init_test(cx, |_| {});
 7124
 7125    let (text, insertion_ranges) = marked_text_ranges(
 7126        indoc! {"
 7127            a.ˇ b
 7128            a.ˇ b
 7129            a.ˇ b
 7130        "},
 7131        false,
 7132    );
 7133
 7134    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7135    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7136
 7137    editor.update_in(cx, |editor, window, cx| {
 7138        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7139
 7140        editor
 7141            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7142            .unwrap();
 7143
 7144        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7145            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7146            assert_eq!(editor.text(cx), expected_text);
 7147            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7148        }
 7149
 7150        assert(
 7151            editor,
 7152            cx,
 7153            indoc! {"
 7154                a.f(«one», two, «three») b
 7155                a.f(«one», two, «three») b
 7156                a.f(«one», two, «three») b
 7157            "},
 7158        );
 7159
 7160        // Can't move earlier than the first tab stop
 7161        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7162        assert(
 7163            editor,
 7164            cx,
 7165            indoc! {"
 7166                a.f(«one», two, «three») b
 7167                a.f(«one», two, «three») b
 7168                a.f(«one», two, «three») b
 7169            "},
 7170        );
 7171
 7172        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7173        assert(
 7174            editor,
 7175            cx,
 7176            indoc! {"
 7177                a.f(one, «two», three) b
 7178                a.f(one, «two», three) b
 7179                a.f(one, «two», three) b
 7180            "},
 7181        );
 7182
 7183        editor.move_to_prev_snippet_tabstop(window, cx);
 7184        assert(
 7185            editor,
 7186            cx,
 7187            indoc! {"
 7188                a.f(«one», two, «three») b
 7189                a.f(«one», two, «three») b
 7190                a.f(«one», two, «three») b
 7191            "},
 7192        );
 7193
 7194        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7195        assert(
 7196            editor,
 7197            cx,
 7198            indoc! {"
 7199                a.f(one, «two», three) b
 7200                a.f(one, «two», three) b
 7201                a.f(one, «two», three) b
 7202            "},
 7203        );
 7204        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7205        assert(
 7206            editor,
 7207            cx,
 7208            indoc! {"
 7209                a.f(one, two, three)ˇ b
 7210                a.f(one, two, three)ˇ b
 7211                a.f(one, two, three)ˇ b
 7212            "},
 7213        );
 7214
 7215        // As soon as the last tab stop is reached, snippet state is gone
 7216        editor.move_to_prev_snippet_tabstop(window, cx);
 7217        assert(
 7218            editor,
 7219            cx,
 7220            indoc! {"
 7221                a.f(one, two, three)ˇ b
 7222                a.f(one, two, three)ˇ b
 7223                a.f(one, two, three)ˇ b
 7224            "},
 7225        );
 7226    });
 7227}
 7228
 7229#[gpui::test]
 7230async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7231    init_test(cx, |_| {});
 7232
 7233    let fs = FakeFs::new(cx.executor());
 7234    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7235
 7236    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7237
 7238    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7239    language_registry.add(rust_lang());
 7240    let mut fake_servers = language_registry.register_fake_lsp(
 7241        "Rust",
 7242        FakeLspAdapter {
 7243            capabilities: lsp::ServerCapabilities {
 7244                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7245                ..Default::default()
 7246            },
 7247            ..Default::default()
 7248        },
 7249    );
 7250
 7251    let buffer = project
 7252        .update(cx, |project, cx| {
 7253            project.open_local_buffer(path!("/file.rs"), cx)
 7254        })
 7255        .await
 7256        .unwrap();
 7257
 7258    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7259    let (editor, cx) = cx.add_window_view(|window, cx| {
 7260        build_editor_with_project(project.clone(), buffer, window, cx)
 7261    });
 7262    editor.update_in(cx, |editor, window, cx| {
 7263        editor.set_text("one\ntwo\nthree\n", window, cx)
 7264    });
 7265    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7266
 7267    cx.executor().start_waiting();
 7268    let fake_server = fake_servers.next().await.unwrap();
 7269
 7270    let save = editor
 7271        .update_in(cx, |editor, window, cx| {
 7272            editor.save(true, project.clone(), window, cx)
 7273        })
 7274        .unwrap();
 7275    fake_server
 7276        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7277            assert_eq!(
 7278                params.text_document.uri,
 7279                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7280            );
 7281            assert_eq!(params.options.tab_size, 4);
 7282            Ok(Some(vec![lsp::TextEdit::new(
 7283                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7284                ", ".to_string(),
 7285            )]))
 7286        })
 7287        .next()
 7288        .await;
 7289    cx.executor().start_waiting();
 7290    save.await;
 7291
 7292    assert_eq!(
 7293        editor.update(cx, |editor, cx| editor.text(cx)),
 7294        "one, two\nthree\n"
 7295    );
 7296    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7297
 7298    editor.update_in(cx, |editor, window, cx| {
 7299        editor.set_text("one\ntwo\nthree\n", window, cx)
 7300    });
 7301    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7302
 7303    // Ensure we can still save even if formatting hangs.
 7304    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7305        assert_eq!(
 7306            params.text_document.uri,
 7307            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7308        );
 7309        futures::future::pending::<()>().await;
 7310        unreachable!()
 7311    });
 7312    let save = editor
 7313        .update_in(cx, |editor, window, cx| {
 7314            editor.save(true, project.clone(), window, cx)
 7315        })
 7316        .unwrap();
 7317    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7318    cx.executor().start_waiting();
 7319    save.await;
 7320    assert_eq!(
 7321        editor.update(cx, |editor, cx| editor.text(cx)),
 7322        "one\ntwo\nthree\n"
 7323    );
 7324    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7325
 7326    // For non-dirty buffer, no formatting request should be sent
 7327    let save = editor
 7328        .update_in(cx, |editor, window, cx| {
 7329            editor.save(true, project.clone(), window, cx)
 7330        })
 7331        .unwrap();
 7332    let _pending_format_request = fake_server
 7333        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7334            panic!("Should not be invoked on non-dirty buffer");
 7335        })
 7336        .next();
 7337    cx.executor().start_waiting();
 7338    save.await;
 7339
 7340    // Set rust language override and assert overridden tabsize is sent to language server
 7341    update_test_language_settings(cx, |settings| {
 7342        settings.languages.insert(
 7343            "Rust".into(),
 7344            LanguageSettingsContent {
 7345                tab_size: NonZeroU32::new(8),
 7346                ..Default::default()
 7347            },
 7348        );
 7349    });
 7350
 7351    editor.update_in(cx, |editor, window, cx| {
 7352        editor.set_text("somehting_new\n", window, cx)
 7353    });
 7354    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7355    let save = editor
 7356        .update_in(cx, |editor, window, cx| {
 7357            editor.save(true, project.clone(), window, cx)
 7358        })
 7359        .unwrap();
 7360    fake_server
 7361        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7362            assert_eq!(
 7363                params.text_document.uri,
 7364                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7365            );
 7366            assert_eq!(params.options.tab_size, 8);
 7367            Ok(Some(vec![]))
 7368        })
 7369        .next()
 7370        .await;
 7371    cx.executor().start_waiting();
 7372    save.await;
 7373}
 7374
 7375#[gpui::test]
 7376async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7377    init_test(cx, |_| {});
 7378
 7379    let cols = 4;
 7380    let rows = 10;
 7381    let sample_text_1 = sample_text(rows, cols, 'a');
 7382    assert_eq!(
 7383        sample_text_1,
 7384        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7385    );
 7386    let sample_text_2 = sample_text(rows, cols, 'l');
 7387    assert_eq!(
 7388        sample_text_2,
 7389        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7390    );
 7391    let sample_text_3 = sample_text(rows, cols, 'v');
 7392    assert_eq!(
 7393        sample_text_3,
 7394        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7395    );
 7396
 7397    let fs = FakeFs::new(cx.executor());
 7398    fs.insert_tree(
 7399        path!("/a"),
 7400        json!({
 7401            "main.rs": sample_text_1,
 7402            "other.rs": sample_text_2,
 7403            "lib.rs": sample_text_3,
 7404        }),
 7405    )
 7406    .await;
 7407
 7408    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7409    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7410    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7411
 7412    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7413    language_registry.add(rust_lang());
 7414    let mut fake_servers = language_registry.register_fake_lsp(
 7415        "Rust",
 7416        FakeLspAdapter {
 7417            capabilities: lsp::ServerCapabilities {
 7418                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7419                ..Default::default()
 7420            },
 7421            ..Default::default()
 7422        },
 7423    );
 7424
 7425    let worktree = project.update(cx, |project, cx| {
 7426        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7427        assert_eq!(worktrees.len(), 1);
 7428        worktrees.pop().unwrap()
 7429    });
 7430    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7431
 7432    let buffer_1 = project
 7433        .update(cx, |project, cx| {
 7434            project.open_buffer((worktree_id, "main.rs"), cx)
 7435        })
 7436        .await
 7437        .unwrap();
 7438    let buffer_2 = project
 7439        .update(cx, |project, cx| {
 7440            project.open_buffer((worktree_id, "other.rs"), cx)
 7441        })
 7442        .await
 7443        .unwrap();
 7444    let buffer_3 = project
 7445        .update(cx, |project, cx| {
 7446            project.open_buffer((worktree_id, "lib.rs"), cx)
 7447        })
 7448        .await
 7449        .unwrap();
 7450
 7451    let multi_buffer = cx.new(|cx| {
 7452        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7453        multi_buffer.push_excerpts(
 7454            buffer_1.clone(),
 7455            [
 7456                ExcerptRange {
 7457                    context: Point::new(0, 0)..Point::new(3, 0),
 7458                    primary: None,
 7459                },
 7460                ExcerptRange {
 7461                    context: Point::new(5, 0)..Point::new(7, 0),
 7462                    primary: None,
 7463                },
 7464                ExcerptRange {
 7465                    context: Point::new(9, 0)..Point::new(10, 4),
 7466                    primary: None,
 7467                },
 7468            ],
 7469            cx,
 7470        );
 7471        multi_buffer.push_excerpts(
 7472            buffer_2.clone(),
 7473            [
 7474                ExcerptRange {
 7475                    context: Point::new(0, 0)..Point::new(3, 0),
 7476                    primary: None,
 7477                },
 7478                ExcerptRange {
 7479                    context: Point::new(5, 0)..Point::new(7, 0),
 7480                    primary: None,
 7481                },
 7482                ExcerptRange {
 7483                    context: Point::new(9, 0)..Point::new(10, 4),
 7484                    primary: None,
 7485                },
 7486            ],
 7487            cx,
 7488        );
 7489        multi_buffer.push_excerpts(
 7490            buffer_3.clone(),
 7491            [
 7492                ExcerptRange {
 7493                    context: Point::new(0, 0)..Point::new(3, 0),
 7494                    primary: None,
 7495                },
 7496                ExcerptRange {
 7497                    context: Point::new(5, 0)..Point::new(7, 0),
 7498                    primary: None,
 7499                },
 7500                ExcerptRange {
 7501                    context: Point::new(9, 0)..Point::new(10, 4),
 7502                    primary: None,
 7503                },
 7504            ],
 7505            cx,
 7506        );
 7507        multi_buffer
 7508    });
 7509    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7510        Editor::new(
 7511            EditorMode::Full,
 7512            multi_buffer,
 7513            Some(project.clone()),
 7514            true,
 7515            window,
 7516            cx,
 7517        )
 7518    });
 7519
 7520    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7521        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7522            s.select_ranges(Some(1..2))
 7523        });
 7524        editor.insert("|one|two|three|", window, cx);
 7525    });
 7526    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7527    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7528        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7529            s.select_ranges(Some(60..70))
 7530        });
 7531        editor.insert("|four|five|six|", window, cx);
 7532    });
 7533    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7534
 7535    // First two buffers should be edited, but not the third one.
 7536    assert_eq!(
 7537        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7538        "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}",
 7539    );
 7540    buffer_1.update(cx, |buffer, _| {
 7541        assert!(buffer.is_dirty());
 7542        assert_eq!(
 7543            buffer.text(),
 7544            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7545        )
 7546    });
 7547    buffer_2.update(cx, |buffer, _| {
 7548        assert!(buffer.is_dirty());
 7549        assert_eq!(
 7550            buffer.text(),
 7551            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7552        )
 7553    });
 7554    buffer_3.update(cx, |buffer, _| {
 7555        assert!(!buffer.is_dirty());
 7556        assert_eq!(buffer.text(), sample_text_3,)
 7557    });
 7558    cx.executor().run_until_parked();
 7559
 7560    cx.executor().start_waiting();
 7561    let save = multi_buffer_editor
 7562        .update_in(cx, |editor, window, cx| {
 7563            editor.save(true, project.clone(), window, cx)
 7564        })
 7565        .unwrap();
 7566
 7567    let fake_server = fake_servers.next().await.unwrap();
 7568    fake_server
 7569        .server
 7570        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7571            Ok(Some(vec![lsp::TextEdit::new(
 7572                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7573                format!("[{} formatted]", params.text_document.uri),
 7574            )]))
 7575        })
 7576        .detach();
 7577    save.await;
 7578
 7579    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7580    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7581    assert_eq!(
 7582        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7583        uri!("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}"),
 7584    );
 7585    buffer_1.update(cx, |buffer, _| {
 7586        assert!(!buffer.is_dirty());
 7587        assert_eq!(
 7588            buffer.text(),
 7589            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7590        )
 7591    });
 7592    buffer_2.update(cx, |buffer, _| {
 7593        assert!(!buffer.is_dirty());
 7594        assert_eq!(
 7595            buffer.text(),
 7596            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7597        )
 7598    });
 7599    buffer_3.update(cx, |buffer, _| {
 7600        assert!(!buffer.is_dirty());
 7601        assert_eq!(buffer.text(), sample_text_3,)
 7602    });
 7603}
 7604
 7605#[gpui::test]
 7606async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7607    init_test(cx, |_| {});
 7608
 7609    let fs = FakeFs::new(cx.executor());
 7610    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7611
 7612    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7613
 7614    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7615    language_registry.add(rust_lang());
 7616    let mut fake_servers = language_registry.register_fake_lsp(
 7617        "Rust",
 7618        FakeLspAdapter {
 7619            capabilities: lsp::ServerCapabilities {
 7620                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7621                ..Default::default()
 7622            },
 7623            ..Default::default()
 7624        },
 7625    );
 7626
 7627    let buffer = project
 7628        .update(cx, |project, cx| {
 7629            project.open_local_buffer(path!("/file.rs"), cx)
 7630        })
 7631        .await
 7632        .unwrap();
 7633
 7634    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7635    let (editor, cx) = cx.add_window_view(|window, cx| {
 7636        build_editor_with_project(project.clone(), buffer, window, cx)
 7637    });
 7638    editor.update_in(cx, |editor, window, cx| {
 7639        editor.set_text("one\ntwo\nthree\n", window, cx)
 7640    });
 7641    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7642
 7643    cx.executor().start_waiting();
 7644    let fake_server = fake_servers.next().await.unwrap();
 7645
 7646    let save = editor
 7647        .update_in(cx, |editor, window, cx| {
 7648            editor.save(true, project.clone(), window, cx)
 7649        })
 7650        .unwrap();
 7651    fake_server
 7652        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7653            assert_eq!(
 7654                params.text_document.uri,
 7655                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7656            );
 7657            assert_eq!(params.options.tab_size, 4);
 7658            Ok(Some(vec![lsp::TextEdit::new(
 7659                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7660                ", ".to_string(),
 7661            )]))
 7662        })
 7663        .next()
 7664        .await;
 7665    cx.executor().start_waiting();
 7666    save.await;
 7667    assert_eq!(
 7668        editor.update(cx, |editor, cx| editor.text(cx)),
 7669        "one, two\nthree\n"
 7670    );
 7671    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7672
 7673    editor.update_in(cx, |editor, window, cx| {
 7674        editor.set_text("one\ntwo\nthree\n", window, cx)
 7675    });
 7676    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7677
 7678    // Ensure we can still save even if formatting hangs.
 7679    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7680        move |params, _| async move {
 7681            assert_eq!(
 7682                params.text_document.uri,
 7683                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7684            );
 7685            futures::future::pending::<()>().await;
 7686            unreachable!()
 7687        },
 7688    );
 7689    let save = editor
 7690        .update_in(cx, |editor, window, cx| {
 7691            editor.save(true, project.clone(), window, cx)
 7692        })
 7693        .unwrap();
 7694    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7695    cx.executor().start_waiting();
 7696    save.await;
 7697    assert_eq!(
 7698        editor.update(cx, |editor, cx| editor.text(cx)),
 7699        "one\ntwo\nthree\n"
 7700    );
 7701    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7702
 7703    // For non-dirty buffer, no formatting request should be sent
 7704    let save = editor
 7705        .update_in(cx, |editor, window, cx| {
 7706            editor.save(true, project.clone(), window, cx)
 7707        })
 7708        .unwrap();
 7709    let _pending_format_request = fake_server
 7710        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7711            panic!("Should not be invoked on non-dirty buffer");
 7712        })
 7713        .next();
 7714    cx.executor().start_waiting();
 7715    save.await;
 7716
 7717    // Set Rust language override and assert overridden tabsize is sent to language server
 7718    update_test_language_settings(cx, |settings| {
 7719        settings.languages.insert(
 7720            "Rust".into(),
 7721            LanguageSettingsContent {
 7722                tab_size: NonZeroU32::new(8),
 7723                ..Default::default()
 7724            },
 7725        );
 7726    });
 7727
 7728    editor.update_in(cx, |editor, window, cx| {
 7729        editor.set_text("somehting_new\n", window, cx)
 7730    });
 7731    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7732    let save = editor
 7733        .update_in(cx, |editor, window, cx| {
 7734            editor.save(true, project.clone(), window, cx)
 7735        })
 7736        .unwrap();
 7737    fake_server
 7738        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7739            assert_eq!(
 7740                params.text_document.uri,
 7741                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7742            );
 7743            assert_eq!(params.options.tab_size, 8);
 7744            Ok(Some(vec![]))
 7745        })
 7746        .next()
 7747        .await;
 7748    cx.executor().start_waiting();
 7749    save.await;
 7750}
 7751
 7752#[gpui::test]
 7753async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7754    init_test(cx, |settings| {
 7755        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7756            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7757        ))
 7758    });
 7759
 7760    let fs = FakeFs::new(cx.executor());
 7761    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7762
 7763    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7764
 7765    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7766    language_registry.add(Arc::new(Language::new(
 7767        LanguageConfig {
 7768            name: "Rust".into(),
 7769            matcher: LanguageMatcher {
 7770                path_suffixes: vec!["rs".to_string()],
 7771                ..Default::default()
 7772            },
 7773            ..LanguageConfig::default()
 7774        },
 7775        Some(tree_sitter_rust::LANGUAGE.into()),
 7776    )));
 7777    update_test_language_settings(cx, |settings| {
 7778        // Enable Prettier formatting for the same buffer, and ensure
 7779        // LSP is called instead of Prettier.
 7780        settings.defaults.prettier = Some(PrettierSettings {
 7781            allowed: true,
 7782            ..PrettierSettings::default()
 7783        });
 7784    });
 7785    let mut fake_servers = language_registry.register_fake_lsp(
 7786        "Rust",
 7787        FakeLspAdapter {
 7788            capabilities: lsp::ServerCapabilities {
 7789                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7790                ..Default::default()
 7791            },
 7792            ..Default::default()
 7793        },
 7794    );
 7795
 7796    let buffer = project
 7797        .update(cx, |project, cx| {
 7798            project.open_local_buffer(path!("/file.rs"), cx)
 7799        })
 7800        .await
 7801        .unwrap();
 7802
 7803    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7804    let (editor, cx) = cx.add_window_view(|window, cx| {
 7805        build_editor_with_project(project.clone(), buffer, window, cx)
 7806    });
 7807    editor.update_in(cx, |editor, window, cx| {
 7808        editor.set_text("one\ntwo\nthree\n", window, cx)
 7809    });
 7810
 7811    cx.executor().start_waiting();
 7812    let fake_server = fake_servers.next().await.unwrap();
 7813
 7814    let format = editor
 7815        .update_in(cx, |editor, window, cx| {
 7816            editor.perform_format(
 7817                project.clone(),
 7818                FormatTrigger::Manual,
 7819                FormatTarget::Buffers,
 7820                window,
 7821                cx,
 7822            )
 7823        })
 7824        .unwrap();
 7825    fake_server
 7826        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7827            assert_eq!(
 7828                params.text_document.uri,
 7829                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7830            );
 7831            assert_eq!(params.options.tab_size, 4);
 7832            Ok(Some(vec![lsp::TextEdit::new(
 7833                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7834                ", ".to_string(),
 7835            )]))
 7836        })
 7837        .next()
 7838        .await;
 7839    cx.executor().start_waiting();
 7840    format.await;
 7841    assert_eq!(
 7842        editor.update(cx, |editor, cx| editor.text(cx)),
 7843        "one, two\nthree\n"
 7844    );
 7845
 7846    editor.update_in(cx, |editor, window, cx| {
 7847        editor.set_text("one\ntwo\nthree\n", window, cx)
 7848    });
 7849    // Ensure we don't lock if formatting hangs.
 7850    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7851        assert_eq!(
 7852            params.text_document.uri,
 7853            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7854        );
 7855        futures::future::pending::<()>().await;
 7856        unreachable!()
 7857    });
 7858    let format = editor
 7859        .update_in(cx, |editor, window, cx| {
 7860            editor.perform_format(
 7861                project,
 7862                FormatTrigger::Manual,
 7863                FormatTarget::Buffers,
 7864                window,
 7865                cx,
 7866            )
 7867        })
 7868        .unwrap();
 7869    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7870    cx.executor().start_waiting();
 7871    format.await;
 7872    assert_eq!(
 7873        editor.update(cx, |editor, cx| editor.text(cx)),
 7874        "one\ntwo\nthree\n"
 7875    );
 7876}
 7877
 7878#[gpui::test]
 7879async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 7880    init_test(cx, |_| {});
 7881
 7882    let mut cx = EditorLspTestContext::new_rust(
 7883        lsp::ServerCapabilities {
 7884            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7885            ..Default::default()
 7886        },
 7887        cx,
 7888    )
 7889    .await;
 7890
 7891    cx.set_state(indoc! {"
 7892        one.twoˇ
 7893    "});
 7894
 7895    // The format request takes a long time. When it completes, it inserts
 7896    // a newline and an indent before the `.`
 7897    cx.lsp
 7898        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7899            let executor = cx.background_executor().clone();
 7900            async move {
 7901                executor.timer(Duration::from_millis(100)).await;
 7902                Ok(Some(vec![lsp::TextEdit {
 7903                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7904                    new_text: "\n    ".into(),
 7905                }]))
 7906            }
 7907        });
 7908
 7909    // Submit a format request.
 7910    let format_1 = cx
 7911        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7912        .unwrap();
 7913    cx.executor().run_until_parked();
 7914
 7915    // Submit a second format request.
 7916    let format_2 = cx
 7917        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7918        .unwrap();
 7919    cx.executor().run_until_parked();
 7920
 7921    // Wait for both format requests to complete
 7922    cx.executor().advance_clock(Duration::from_millis(200));
 7923    cx.executor().start_waiting();
 7924    format_1.await.unwrap();
 7925    cx.executor().start_waiting();
 7926    format_2.await.unwrap();
 7927
 7928    // The formatting edits only happens once.
 7929    cx.assert_editor_state(indoc! {"
 7930        one
 7931            .twoˇ
 7932    "});
 7933}
 7934
 7935#[gpui::test]
 7936async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 7937    init_test(cx, |settings| {
 7938        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7939    });
 7940
 7941    let mut cx = EditorLspTestContext::new_rust(
 7942        lsp::ServerCapabilities {
 7943            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7944            ..Default::default()
 7945        },
 7946        cx,
 7947    )
 7948    .await;
 7949
 7950    // Set up a buffer white some trailing whitespace and no trailing newline.
 7951    cx.set_state(
 7952        &[
 7953            "one ",   //
 7954            "twoˇ",   //
 7955            "three ", //
 7956            "four",   //
 7957        ]
 7958        .join("\n"),
 7959    );
 7960
 7961    // Submit a format request.
 7962    let format = cx
 7963        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7964        .unwrap();
 7965
 7966    // Record which buffer changes have been sent to the language server
 7967    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7968    cx.lsp
 7969        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7970            let buffer_changes = buffer_changes.clone();
 7971            move |params, _| {
 7972                buffer_changes.lock().extend(
 7973                    params
 7974                        .content_changes
 7975                        .into_iter()
 7976                        .map(|e| (e.range.unwrap(), e.text)),
 7977                );
 7978            }
 7979        });
 7980
 7981    // Handle formatting requests to the language server.
 7982    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7983        let buffer_changes = buffer_changes.clone();
 7984        move |_, _| {
 7985            // When formatting is requested, trailing whitespace has already been stripped,
 7986            // and the trailing newline has already been added.
 7987            assert_eq!(
 7988                &buffer_changes.lock()[1..],
 7989                &[
 7990                    (
 7991                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7992                        "".into()
 7993                    ),
 7994                    (
 7995                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7996                        "".into()
 7997                    ),
 7998                    (
 7999                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8000                        "\n".into()
 8001                    ),
 8002                ]
 8003            );
 8004
 8005            // Insert blank lines between each line of the buffer.
 8006            async move {
 8007                Ok(Some(vec![
 8008                    lsp::TextEdit {
 8009                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8010                        new_text: "\n".into(),
 8011                    },
 8012                    lsp::TextEdit {
 8013                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8014                        new_text: "\n".into(),
 8015                    },
 8016                ]))
 8017            }
 8018        }
 8019    });
 8020
 8021    // After formatting the buffer, the trailing whitespace is stripped,
 8022    // a newline is appended, and the edits provided by the language server
 8023    // have been applied.
 8024    format.await.unwrap();
 8025    cx.assert_editor_state(
 8026        &[
 8027            "one",   //
 8028            "",      //
 8029            "twoˇ",  //
 8030            "",      //
 8031            "three", //
 8032            "four",  //
 8033            "",      //
 8034        ]
 8035        .join("\n"),
 8036    );
 8037
 8038    // Undoing the formatting undoes the trailing whitespace removal, the
 8039    // trailing newline, and the LSP edits.
 8040    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8041    cx.assert_editor_state(
 8042        &[
 8043            "one ",   //
 8044            "twoˇ",   //
 8045            "three ", //
 8046            "four",   //
 8047        ]
 8048        .join("\n"),
 8049    );
 8050}
 8051
 8052#[gpui::test]
 8053async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8054    cx: &mut TestAppContext,
 8055) {
 8056    init_test(cx, |_| {});
 8057
 8058    cx.update(|cx| {
 8059        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8060            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8061                settings.auto_signature_help = Some(true);
 8062            });
 8063        });
 8064    });
 8065
 8066    let mut cx = EditorLspTestContext::new_rust(
 8067        lsp::ServerCapabilities {
 8068            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8069                ..Default::default()
 8070            }),
 8071            ..Default::default()
 8072        },
 8073        cx,
 8074    )
 8075    .await;
 8076
 8077    let language = Language::new(
 8078        LanguageConfig {
 8079            name: "Rust".into(),
 8080            brackets: BracketPairConfig {
 8081                pairs: vec![
 8082                    BracketPair {
 8083                        start: "{".to_string(),
 8084                        end: "}".to_string(),
 8085                        close: true,
 8086                        surround: true,
 8087                        newline: true,
 8088                    },
 8089                    BracketPair {
 8090                        start: "(".to_string(),
 8091                        end: ")".to_string(),
 8092                        close: true,
 8093                        surround: true,
 8094                        newline: true,
 8095                    },
 8096                    BracketPair {
 8097                        start: "/*".to_string(),
 8098                        end: " */".to_string(),
 8099                        close: true,
 8100                        surround: true,
 8101                        newline: true,
 8102                    },
 8103                    BracketPair {
 8104                        start: "[".to_string(),
 8105                        end: "]".to_string(),
 8106                        close: false,
 8107                        surround: false,
 8108                        newline: true,
 8109                    },
 8110                    BracketPair {
 8111                        start: "\"".to_string(),
 8112                        end: "\"".to_string(),
 8113                        close: true,
 8114                        surround: true,
 8115                        newline: false,
 8116                    },
 8117                    BracketPair {
 8118                        start: "<".to_string(),
 8119                        end: ">".to_string(),
 8120                        close: false,
 8121                        surround: true,
 8122                        newline: true,
 8123                    },
 8124                ],
 8125                ..Default::default()
 8126            },
 8127            autoclose_before: "})]".to_string(),
 8128            ..Default::default()
 8129        },
 8130        Some(tree_sitter_rust::LANGUAGE.into()),
 8131    );
 8132    let language = Arc::new(language);
 8133
 8134    cx.language_registry().add(language.clone());
 8135    cx.update_buffer(|buffer, cx| {
 8136        buffer.set_language(Some(language), cx);
 8137    });
 8138
 8139    cx.set_state(
 8140        &r#"
 8141            fn main() {
 8142                sampleˇ
 8143            }
 8144        "#
 8145        .unindent(),
 8146    );
 8147
 8148    cx.update_editor(|editor, window, cx| {
 8149        editor.handle_input("(", window, cx);
 8150    });
 8151    cx.assert_editor_state(
 8152        &"
 8153            fn main() {
 8154                sample(ˇ)
 8155            }
 8156        "
 8157        .unindent(),
 8158    );
 8159
 8160    let mocked_response = lsp::SignatureHelp {
 8161        signatures: vec![lsp::SignatureInformation {
 8162            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8163            documentation: None,
 8164            parameters: Some(vec![
 8165                lsp::ParameterInformation {
 8166                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8167                    documentation: None,
 8168                },
 8169                lsp::ParameterInformation {
 8170                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8171                    documentation: None,
 8172                },
 8173            ]),
 8174            active_parameter: None,
 8175        }],
 8176        active_signature: Some(0),
 8177        active_parameter: Some(0),
 8178    };
 8179    handle_signature_help_request(&mut cx, mocked_response).await;
 8180
 8181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8182        .await;
 8183
 8184    cx.editor(|editor, _, _| {
 8185        let signature_help_state = editor.signature_help_state.popover().cloned();
 8186        assert_eq!(
 8187            signature_help_state.unwrap().label,
 8188            "param1: u8, param2: u8"
 8189        );
 8190    });
 8191}
 8192
 8193#[gpui::test]
 8194async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8195    init_test(cx, |_| {});
 8196
 8197    cx.update(|cx| {
 8198        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8199            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8200                settings.auto_signature_help = Some(false);
 8201                settings.show_signature_help_after_edits = Some(false);
 8202            });
 8203        });
 8204    });
 8205
 8206    let mut cx = EditorLspTestContext::new_rust(
 8207        lsp::ServerCapabilities {
 8208            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8209                ..Default::default()
 8210            }),
 8211            ..Default::default()
 8212        },
 8213        cx,
 8214    )
 8215    .await;
 8216
 8217    let language = Language::new(
 8218        LanguageConfig {
 8219            name: "Rust".into(),
 8220            brackets: BracketPairConfig {
 8221                pairs: vec![
 8222                    BracketPair {
 8223                        start: "{".to_string(),
 8224                        end: "}".to_string(),
 8225                        close: true,
 8226                        surround: true,
 8227                        newline: true,
 8228                    },
 8229                    BracketPair {
 8230                        start: "(".to_string(),
 8231                        end: ")".to_string(),
 8232                        close: true,
 8233                        surround: true,
 8234                        newline: true,
 8235                    },
 8236                    BracketPair {
 8237                        start: "/*".to_string(),
 8238                        end: " */".to_string(),
 8239                        close: true,
 8240                        surround: true,
 8241                        newline: true,
 8242                    },
 8243                    BracketPair {
 8244                        start: "[".to_string(),
 8245                        end: "]".to_string(),
 8246                        close: false,
 8247                        surround: false,
 8248                        newline: true,
 8249                    },
 8250                    BracketPair {
 8251                        start: "\"".to_string(),
 8252                        end: "\"".to_string(),
 8253                        close: true,
 8254                        surround: true,
 8255                        newline: false,
 8256                    },
 8257                    BracketPair {
 8258                        start: "<".to_string(),
 8259                        end: ">".to_string(),
 8260                        close: false,
 8261                        surround: true,
 8262                        newline: true,
 8263                    },
 8264                ],
 8265                ..Default::default()
 8266            },
 8267            autoclose_before: "})]".to_string(),
 8268            ..Default::default()
 8269        },
 8270        Some(tree_sitter_rust::LANGUAGE.into()),
 8271    );
 8272    let language = Arc::new(language);
 8273
 8274    cx.language_registry().add(language.clone());
 8275    cx.update_buffer(|buffer, cx| {
 8276        buffer.set_language(Some(language), cx);
 8277    });
 8278
 8279    // Ensure that signature_help is not called when no signature help is enabled.
 8280    cx.set_state(
 8281        &r#"
 8282            fn main() {
 8283                sampleˇ
 8284            }
 8285        "#
 8286        .unindent(),
 8287    );
 8288    cx.update_editor(|editor, window, cx| {
 8289        editor.handle_input("(", window, cx);
 8290    });
 8291    cx.assert_editor_state(
 8292        &"
 8293            fn main() {
 8294                sample(ˇ)
 8295            }
 8296        "
 8297        .unindent(),
 8298    );
 8299    cx.editor(|editor, _, _| {
 8300        assert!(editor.signature_help_state.task().is_none());
 8301    });
 8302
 8303    let mocked_response = lsp::SignatureHelp {
 8304        signatures: vec![lsp::SignatureInformation {
 8305            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8306            documentation: None,
 8307            parameters: Some(vec![
 8308                lsp::ParameterInformation {
 8309                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8310                    documentation: None,
 8311                },
 8312                lsp::ParameterInformation {
 8313                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8314                    documentation: None,
 8315                },
 8316            ]),
 8317            active_parameter: None,
 8318        }],
 8319        active_signature: Some(0),
 8320        active_parameter: Some(0),
 8321    };
 8322
 8323    // Ensure that signature_help is called when enabled afte edits
 8324    cx.update(|_, cx| {
 8325        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8326            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8327                settings.auto_signature_help = Some(false);
 8328                settings.show_signature_help_after_edits = Some(true);
 8329            });
 8330        });
 8331    });
 8332    cx.set_state(
 8333        &r#"
 8334            fn main() {
 8335                sampleˇ
 8336            }
 8337        "#
 8338        .unindent(),
 8339    );
 8340    cx.update_editor(|editor, window, cx| {
 8341        editor.handle_input("(", window, cx);
 8342    });
 8343    cx.assert_editor_state(
 8344        &"
 8345            fn main() {
 8346                sample(ˇ)
 8347            }
 8348        "
 8349        .unindent(),
 8350    );
 8351    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8352    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8353        .await;
 8354    cx.update_editor(|editor, _, _| {
 8355        let signature_help_state = editor.signature_help_state.popover().cloned();
 8356        assert!(signature_help_state.is_some());
 8357        assert_eq!(
 8358            signature_help_state.unwrap().label,
 8359            "param1: u8, param2: u8"
 8360        );
 8361        editor.signature_help_state = SignatureHelpState::default();
 8362    });
 8363
 8364    // Ensure that signature_help is called when auto signature help override is enabled
 8365    cx.update(|_, cx| {
 8366        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8367            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8368                settings.auto_signature_help = Some(true);
 8369                settings.show_signature_help_after_edits = Some(false);
 8370            });
 8371        });
 8372    });
 8373    cx.set_state(
 8374        &r#"
 8375            fn main() {
 8376                sampleˇ
 8377            }
 8378        "#
 8379        .unindent(),
 8380    );
 8381    cx.update_editor(|editor, window, cx| {
 8382        editor.handle_input("(", window, cx);
 8383    });
 8384    cx.assert_editor_state(
 8385        &"
 8386            fn main() {
 8387                sample(ˇ)
 8388            }
 8389        "
 8390        .unindent(),
 8391    );
 8392    handle_signature_help_request(&mut cx, mocked_response).await;
 8393    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8394        .await;
 8395    cx.editor(|editor, _, _| {
 8396        let signature_help_state = editor.signature_help_state.popover().cloned();
 8397        assert!(signature_help_state.is_some());
 8398        assert_eq!(
 8399            signature_help_state.unwrap().label,
 8400            "param1: u8, param2: u8"
 8401        );
 8402    });
 8403}
 8404
 8405#[gpui::test]
 8406async fn test_signature_help(cx: &mut TestAppContext) {
 8407    init_test(cx, |_| {});
 8408    cx.update(|cx| {
 8409        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8410            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8411                settings.auto_signature_help = Some(true);
 8412            });
 8413        });
 8414    });
 8415
 8416    let mut cx = EditorLspTestContext::new_rust(
 8417        lsp::ServerCapabilities {
 8418            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8419                ..Default::default()
 8420            }),
 8421            ..Default::default()
 8422        },
 8423        cx,
 8424    )
 8425    .await;
 8426
 8427    // A test that directly calls `show_signature_help`
 8428    cx.update_editor(|editor, window, cx| {
 8429        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8430    });
 8431
 8432    let mocked_response = lsp::SignatureHelp {
 8433        signatures: vec![lsp::SignatureInformation {
 8434            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8435            documentation: None,
 8436            parameters: Some(vec![
 8437                lsp::ParameterInformation {
 8438                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8439                    documentation: None,
 8440                },
 8441                lsp::ParameterInformation {
 8442                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8443                    documentation: None,
 8444                },
 8445            ]),
 8446            active_parameter: None,
 8447        }],
 8448        active_signature: Some(0),
 8449        active_parameter: Some(0),
 8450    };
 8451    handle_signature_help_request(&mut cx, mocked_response).await;
 8452
 8453    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8454        .await;
 8455
 8456    cx.editor(|editor, _, _| {
 8457        let signature_help_state = editor.signature_help_state.popover().cloned();
 8458        assert!(signature_help_state.is_some());
 8459        assert_eq!(
 8460            signature_help_state.unwrap().label,
 8461            "param1: u8, param2: u8"
 8462        );
 8463    });
 8464
 8465    // When exiting outside from inside the brackets, `signature_help` is closed.
 8466    cx.set_state(indoc! {"
 8467        fn main() {
 8468            sample(ˇ);
 8469        }
 8470
 8471        fn sample(param1: u8, param2: u8) {}
 8472    "});
 8473
 8474    cx.update_editor(|editor, window, cx| {
 8475        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8476    });
 8477
 8478    let mocked_response = lsp::SignatureHelp {
 8479        signatures: Vec::new(),
 8480        active_signature: None,
 8481        active_parameter: None,
 8482    };
 8483    handle_signature_help_request(&mut cx, mocked_response).await;
 8484
 8485    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8486        .await;
 8487
 8488    cx.editor(|editor, _, _| {
 8489        assert!(!editor.signature_help_state.is_shown());
 8490    });
 8491
 8492    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8493    cx.set_state(indoc! {"
 8494        fn main() {
 8495            sample(ˇ);
 8496        }
 8497
 8498        fn sample(param1: u8, param2: u8) {}
 8499    "});
 8500
 8501    let mocked_response = lsp::SignatureHelp {
 8502        signatures: vec![lsp::SignatureInformation {
 8503            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8504            documentation: None,
 8505            parameters: Some(vec![
 8506                lsp::ParameterInformation {
 8507                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8508                    documentation: None,
 8509                },
 8510                lsp::ParameterInformation {
 8511                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8512                    documentation: None,
 8513                },
 8514            ]),
 8515            active_parameter: None,
 8516        }],
 8517        active_signature: Some(0),
 8518        active_parameter: Some(0),
 8519    };
 8520    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8521    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8522        .await;
 8523    cx.editor(|editor, _, _| {
 8524        assert!(editor.signature_help_state.is_shown());
 8525    });
 8526
 8527    // Restore the popover with more parameter input
 8528    cx.set_state(indoc! {"
 8529        fn main() {
 8530            sample(param1, param2ˇ);
 8531        }
 8532
 8533        fn sample(param1: u8, param2: u8) {}
 8534    "});
 8535
 8536    let mocked_response = lsp::SignatureHelp {
 8537        signatures: vec![lsp::SignatureInformation {
 8538            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8539            documentation: None,
 8540            parameters: Some(vec![
 8541                lsp::ParameterInformation {
 8542                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8543                    documentation: None,
 8544                },
 8545                lsp::ParameterInformation {
 8546                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8547                    documentation: None,
 8548                },
 8549            ]),
 8550            active_parameter: None,
 8551        }],
 8552        active_signature: Some(0),
 8553        active_parameter: Some(1),
 8554    };
 8555    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8556    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8557        .await;
 8558
 8559    // When selecting a range, the popover is gone.
 8560    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8561    cx.update_editor(|editor, window, cx| {
 8562        editor.change_selections(None, window, cx, |s| {
 8563            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8564        })
 8565    });
 8566    cx.assert_editor_state(indoc! {"
 8567        fn main() {
 8568            sample(param1, «ˇparam2»);
 8569        }
 8570
 8571        fn sample(param1: u8, param2: u8) {}
 8572    "});
 8573    cx.editor(|editor, _, _| {
 8574        assert!(!editor.signature_help_state.is_shown());
 8575    });
 8576
 8577    // When unselecting again, the popover is back if within the brackets.
 8578    cx.update_editor(|editor, window, cx| {
 8579        editor.change_selections(None, window, cx, |s| {
 8580            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8581        })
 8582    });
 8583    cx.assert_editor_state(indoc! {"
 8584        fn main() {
 8585            sample(param1, ˇparam2);
 8586        }
 8587
 8588        fn sample(param1: u8, param2: u8) {}
 8589    "});
 8590    handle_signature_help_request(&mut cx, mocked_response).await;
 8591    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8592        .await;
 8593    cx.editor(|editor, _, _| {
 8594        assert!(editor.signature_help_state.is_shown());
 8595    });
 8596
 8597    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8598    cx.update_editor(|editor, window, cx| {
 8599        editor.change_selections(None, window, cx, |s| {
 8600            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8601            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8602        })
 8603    });
 8604    cx.assert_editor_state(indoc! {"
 8605        fn main() {
 8606            sample(param1, ˇparam2);
 8607        }
 8608
 8609        fn sample(param1: u8, param2: u8) {}
 8610    "});
 8611
 8612    let mocked_response = lsp::SignatureHelp {
 8613        signatures: vec![lsp::SignatureInformation {
 8614            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8615            documentation: None,
 8616            parameters: Some(vec![
 8617                lsp::ParameterInformation {
 8618                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8619                    documentation: None,
 8620                },
 8621                lsp::ParameterInformation {
 8622                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8623                    documentation: None,
 8624                },
 8625            ]),
 8626            active_parameter: None,
 8627        }],
 8628        active_signature: Some(0),
 8629        active_parameter: Some(1),
 8630    };
 8631    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8632    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8633        .await;
 8634    cx.update_editor(|editor, _, cx| {
 8635        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8636    });
 8637    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8638        .await;
 8639    cx.update_editor(|editor, window, cx| {
 8640        editor.change_selections(None, window, cx, |s| {
 8641            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8642        })
 8643    });
 8644    cx.assert_editor_state(indoc! {"
 8645        fn main() {
 8646            sample(param1, «ˇparam2»);
 8647        }
 8648
 8649        fn sample(param1: u8, param2: u8) {}
 8650    "});
 8651    cx.update_editor(|editor, window, cx| {
 8652        editor.change_selections(None, window, cx, |s| {
 8653            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8654        })
 8655    });
 8656    cx.assert_editor_state(indoc! {"
 8657        fn main() {
 8658            sample(param1, ˇparam2);
 8659        }
 8660
 8661        fn sample(param1: u8, param2: u8) {}
 8662    "});
 8663    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8664        .await;
 8665}
 8666
 8667#[gpui::test]
 8668async fn test_completion(cx: &mut TestAppContext) {
 8669    init_test(cx, |_| {});
 8670
 8671    let mut cx = EditorLspTestContext::new_rust(
 8672        lsp::ServerCapabilities {
 8673            completion_provider: Some(lsp::CompletionOptions {
 8674                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8675                resolve_provider: Some(true),
 8676                ..Default::default()
 8677            }),
 8678            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8679            ..Default::default()
 8680        },
 8681        cx,
 8682    )
 8683    .await;
 8684    let counter = Arc::new(AtomicUsize::new(0));
 8685
 8686    cx.set_state(indoc! {"
 8687        oneˇ
 8688        two
 8689        three
 8690    "});
 8691    cx.simulate_keystroke(".");
 8692    handle_completion_request(
 8693        &mut cx,
 8694        indoc! {"
 8695            one.|<>
 8696            two
 8697            three
 8698        "},
 8699        vec!["first_completion", "second_completion"],
 8700        counter.clone(),
 8701    )
 8702    .await;
 8703    cx.condition(|editor, _| editor.context_menu_visible())
 8704        .await;
 8705    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8706
 8707    let _handler = handle_signature_help_request(
 8708        &mut cx,
 8709        lsp::SignatureHelp {
 8710            signatures: vec![lsp::SignatureInformation {
 8711                label: "test signature".to_string(),
 8712                documentation: None,
 8713                parameters: Some(vec![lsp::ParameterInformation {
 8714                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8715                    documentation: None,
 8716                }]),
 8717                active_parameter: None,
 8718            }],
 8719            active_signature: None,
 8720            active_parameter: None,
 8721        },
 8722    );
 8723    cx.update_editor(|editor, window, cx| {
 8724        assert!(
 8725            !editor.signature_help_state.is_shown(),
 8726            "No signature help was called for"
 8727        );
 8728        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8729    });
 8730    cx.run_until_parked();
 8731    cx.update_editor(|editor, _, _| {
 8732        assert!(
 8733            !editor.signature_help_state.is_shown(),
 8734            "No signature help should be shown when completions menu is open"
 8735        );
 8736    });
 8737
 8738    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8739        editor.context_menu_next(&Default::default(), window, cx);
 8740        editor
 8741            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8742            .unwrap()
 8743    });
 8744    cx.assert_editor_state(indoc! {"
 8745        one.second_completionˇ
 8746        two
 8747        three
 8748    "});
 8749
 8750    handle_resolve_completion_request(
 8751        &mut cx,
 8752        Some(vec![
 8753            (
 8754                //This overlaps with the primary completion edit which is
 8755                //misbehavior from the LSP spec, test that we filter it out
 8756                indoc! {"
 8757                    one.second_ˇcompletion
 8758                    two
 8759                    threeˇ
 8760                "},
 8761                "overlapping additional edit",
 8762            ),
 8763            (
 8764                indoc! {"
 8765                    one.second_completion
 8766                    two
 8767                    threeˇ
 8768                "},
 8769                "\nadditional edit",
 8770            ),
 8771        ]),
 8772    )
 8773    .await;
 8774    apply_additional_edits.await.unwrap();
 8775    cx.assert_editor_state(indoc! {"
 8776        one.second_completionˇ
 8777        two
 8778        three
 8779        additional edit
 8780    "});
 8781
 8782    cx.set_state(indoc! {"
 8783        one.second_completion
 8784        twoˇ
 8785        threeˇ
 8786        additional edit
 8787    "});
 8788    cx.simulate_keystroke(" ");
 8789    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8790    cx.simulate_keystroke("s");
 8791    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8792
 8793    cx.assert_editor_state(indoc! {"
 8794        one.second_completion
 8795        two sˇ
 8796        three sˇ
 8797        additional edit
 8798    "});
 8799    handle_completion_request(
 8800        &mut cx,
 8801        indoc! {"
 8802            one.second_completion
 8803            two s
 8804            three <s|>
 8805            additional edit
 8806        "},
 8807        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8808        counter.clone(),
 8809    )
 8810    .await;
 8811    cx.condition(|editor, _| editor.context_menu_visible())
 8812        .await;
 8813    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8814
 8815    cx.simulate_keystroke("i");
 8816
 8817    handle_completion_request(
 8818        &mut cx,
 8819        indoc! {"
 8820            one.second_completion
 8821            two si
 8822            three <si|>
 8823            additional edit
 8824        "},
 8825        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8826        counter.clone(),
 8827    )
 8828    .await;
 8829    cx.condition(|editor, _| editor.context_menu_visible())
 8830        .await;
 8831    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8832
 8833    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8834        editor
 8835            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8836            .unwrap()
 8837    });
 8838    cx.assert_editor_state(indoc! {"
 8839        one.second_completion
 8840        two sixth_completionˇ
 8841        three sixth_completionˇ
 8842        additional edit
 8843    "});
 8844
 8845    apply_additional_edits.await.unwrap();
 8846
 8847    update_test_language_settings(&mut cx, |settings| {
 8848        settings.defaults.show_completions_on_input = Some(false);
 8849    });
 8850    cx.set_state("editorˇ");
 8851    cx.simulate_keystroke(".");
 8852    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8853    cx.simulate_keystroke("c");
 8854    cx.simulate_keystroke("l");
 8855    cx.simulate_keystroke("o");
 8856    cx.assert_editor_state("editor.cloˇ");
 8857    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8858    cx.update_editor(|editor, window, cx| {
 8859        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8860    });
 8861    handle_completion_request(
 8862        &mut cx,
 8863        "editor.<clo|>",
 8864        vec!["close", "clobber"],
 8865        counter.clone(),
 8866    )
 8867    .await;
 8868    cx.condition(|editor, _| editor.context_menu_visible())
 8869        .await;
 8870    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8871
 8872    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8873        editor
 8874            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8875            .unwrap()
 8876    });
 8877    cx.assert_editor_state("editor.closeˇ");
 8878    handle_resolve_completion_request(&mut cx, None).await;
 8879    apply_additional_edits.await.unwrap();
 8880}
 8881
 8882#[gpui::test]
 8883async fn test_multiline_completion(cx: &mut TestAppContext) {
 8884    init_test(cx, |_| {});
 8885
 8886    let fs = FakeFs::new(cx.executor());
 8887    fs.insert_tree(
 8888        path!("/a"),
 8889        json!({
 8890            "main.ts": "a",
 8891        }),
 8892    )
 8893    .await;
 8894
 8895    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8896    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8897    let typescript_language = Arc::new(Language::new(
 8898        LanguageConfig {
 8899            name: "TypeScript".into(),
 8900            matcher: LanguageMatcher {
 8901                path_suffixes: vec!["ts".to_string()],
 8902                ..LanguageMatcher::default()
 8903            },
 8904            line_comments: vec!["// ".into()],
 8905            ..LanguageConfig::default()
 8906        },
 8907        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8908    ));
 8909    language_registry.add(typescript_language.clone());
 8910    let mut fake_servers = language_registry.register_fake_lsp(
 8911        "TypeScript",
 8912        FakeLspAdapter {
 8913            capabilities: lsp::ServerCapabilities {
 8914                completion_provider: Some(lsp::CompletionOptions {
 8915                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8916                    ..lsp::CompletionOptions::default()
 8917                }),
 8918                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8919                ..lsp::ServerCapabilities::default()
 8920            },
 8921            // Emulate vtsls label generation
 8922            label_for_completion: Some(Box::new(|item, _| {
 8923                let text = if let Some(description) = item
 8924                    .label_details
 8925                    .as_ref()
 8926                    .and_then(|label_details| label_details.description.as_ref())
 8927                {
 8928                    format!("{} {}", item.label, description)
 8929                } else if let Some(detail) = &item.detail {
 8930                    format!("{} {}", item.label, detail)
 8931                } else {
 8932                    item.label.clone()
 8933                };
 8934                let len = text.len();
 8935                Some(language::CodeLabel {
 8936                    text,
 8937                    runs: Vec::new(),
 8938                    filter_range: 0..len,
 8939                })
 8940            })),
 8941            ..FakeLspAdapter::default()
 8942        },
 8943    );
 8944    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8945    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8946    let worktree_id = workspace
 8947        .update(cx, |workspace, _window, cx| {
 8948            workspace.project().update(cx, |project, cx| {
 8949                project.worktrees(cx).next().unwrap().read(cx).id()
 8950            })
 8951        })
 8952        .unwrap();
 8953    let _buffer = project
 8954        .update(cx, |project, cx| {
 8955            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8956        })
 8957        .await
 8958        .unwrap();
 8959    let editor = workspace
 8960        .update(cx, |workspace, window, cx| {
 8961            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8962        })
 8963        .unwrap()
 8964        .await
 8965        .unwrap()
 8966        .downcast::<Editor>()
 8967        .unwrap();
 8968    let fake_server = fake_servers.next().await.unwrap();
 8969
 8970    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8971    let multiline_label_2 = "a\nb\nc\n";
 8972    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8973    let multiline_description = "d\ne\nf\n";
 8974    let multiline_detail_2 = "g\nh\ni\n";
 8975
 8976    let mut completion_handle =
 8977        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8978            Ok(Some(lsp::CompletionResponse::Array(vec![
 8979                lsp::CompletionItem {
 8980                    label: multiline_label.to_string(),
 8981                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8982                        range: lsp::Range {
 8983                            start: lsp::Position {
 8984                                line: params.text_document_position.position.line,
 8985                                character: params.text_document_position.position.character,
 8986                            },
 8987                            end: lsp::Position {
 8988                                line: params.text_document_position.position.line,
 8989                                character: params.text_document_position.position.character,
 8990                            },
 8991                        },
 8992                        new_text: "new_text_1".to_string(),
 8993                    })),
 8994                    ..lsp::CompletionItem::default()
 8995                },
 8996                lsp::CompletionItem {
 8997                    label: "single line label 1".to_string(),
 8998                    detail: Some(multiline_detail.to_string()),
 8999                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9000                        range: lsp::Range {
 9001                            start: lsp::Position {
 9002                                line: params.text_document_position.position.line,
 9003                                character: params.text_document_position.position.character,
 9004                            },
 9005                            end: lsp::Position {
 9006                                line: params.text_document_position.position.line,
 9007                                character: params.text_document_position.position.character,
 9008                            },
 9009                        },
 9010                        new_text: "new_text_2".to_string(),
 9011                    })),
 9012                    ..lsp::CompletionItem::default()
 9013                },
 9014                lsp::CompletionItem {
 9015                    label: "single line label 2".to_string(),
 9016                    label_details: Some(lsp::CompletionItemLabelDetails {
 9017                        description: Some(multiline_description.to_string()),
 9018                        detail: None,
 9019                    }),
 9020                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9021                        range: lsp::Range {
 9022                            start: lsp::Position {
 9023                                line: params.text_document_position.position.line,
 9024                                character: params.text_document_position.position.character,
 9025                            },
 9026                            end: lsp::Position {
 9027                                line: params.text_document_position.position.line,
 9028                                character: params.text_document_position.position.character,
 9029                            },
 9030                        },
 9031                        new_text: "new_text_2".to_string(),
 9032                    })),
 9033                    ..lsp::CompletionItem::default()
 9034                },
 9035                lsp::CompletionItem {
 9036                    label: multiline_label_2.to_string(),
 9037                    detail: Some(multiline_detail_2.to_string()),
 9038                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9039                        range: lsp::Range {
 9040                            start: lsp::Position {
 9041                                line: params.text_document_position.position.line,
 9042                                character: params.text_document_position.position.character,
 9043                            },
 9044                            end: lsp::Position {
 9045                                line: params.text_document_position.position.line,
 9046                                character: params.text_document_position.position.character,
 9047                            },
 9048                        },
 9049                        new_text: "new_text_3".to_string(),
 9050                    })),
 9051                    ..lsp::CompletionItem::default()
 9052                },
 9053                lsp::CompletionItem {
 9054                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9055                    detail: Some(
 9056                        "Details with many     spaces and \t but without newlines".to_string(),
 9057                    ),
 9058                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9059                        range: lsp::Range {
 9060                            start: lsp::Position {
 9061                                line: params.text_document_position.position.line,
 9062                                character: params.text_document_position.position.character,
 9063                            },
 9064                            end: lsp::Position {
 9065                                line: params.text_document_position.position.line,
 9066                                character: params.text_document_position.position.character,
 9067                            },
 9068                        },
 9069                        new_text: "new_text_4".to_string(),
 9070                    })),
 9071                    ..lsp::CompletionItem::default()
 9072                },
 9073            ])))
 9074        });
 9075
 9076    editor.update_in(cx, |editor, window, cx| {
 9077        cx.focus_self(window);
 9078        editor.move_to_end(&MoveToEnd, window, cx);
 9079        editor.handle_input(".", window, cx);
 9080    });
 9081    cx.run_until_parked();
 9082    completion_handle.next().await.unwrap();
 9083
 9084    editor.update(cx, |editor, _| {
 9085        assert!(editor.context_menu_visible());
 9086        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9087        {
 9088            let completion_labels = menu
 9089                .completions
 9090                .borrow()
 9091                .iter()
 9092                .map(|c| c.label.text.clone())
 9093                .collect::<Vec<_>>();
 9094            assert_eq!(
 9095                completion_labels,
 9096                &[
 9097                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9098                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9099                    "single line label 2 d e f ",
 9100                    "a b c g h i ",
 9101                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9102                ],
 9103                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9104            );
 9105
 9106            for completion in menu
 9107                .completions
 9108                .borrow()
 9109                .iter() {
 9110                    assert_eq!(
 9111                        completion.label.filter_range,
 9112                        0..completion.label.text.len(),
 9113                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9114                    );
 9115                }
 9116
 9117        } else {
 9118            panic!("expected completion menu to be open");
 9119        }
 9120    });
 9121}
 9122
 9123#[gpui::test]
 9124async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9125    init_test(cx, |_| {});
 9126    let mut cx = EditorLspTestContext::new_rust(
 9127        lsp::ServerCapabilities {
 9128            completion_provider: Some(lsp::CompletionOptions {
 9129                trigger_characters: Some(vec![".".to_string()]),
 9130                ..Default::default()
 9131            }),
 9132            ..Default::default()
 9133        },
 9134        cx,
 9135    )
 9136    .await;
 9137    cx.lsp
 9138        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9139            Ok(Some(lsp::CompletionResponse::Array(vec![
 9140                lsp::CompletionItem {
 9141                    label: "first".into(),
 9142                    ..Default::default()
 9143                },
 9144                lsp::CompletionItem {
 9145                    label: "last".into(),
 9146                    ..Default::default()
 9147                },
 9148            ])))
 9149        });
 9150    cx.set_state("variableˇ");
 9151    cx.simulate_keystroke(".");
 9152    cx.executor().run_until_parked();
 9153
 9154    cx.update_editor(|editor, _, _| {
 9155        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9156        {
 9157            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9158        } else {
 9159            panic!("expected completion menu to be open");
 9160        }
 9161    });
 9162
 9163    cx.update_editor(|editor, window, cx| {
 9164        editor.move_page_down(&MovePageDown::default(), window, cx);
 9165        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9166        {
 9167            assert!(
 9168                menu.selected_item == 1,
 9169                "expected PageDown to select the last item from the context menu"
 9170            );
 9171        } else {
 9172            panic!("expected completion menu to stay open after PageDown");
 9173        }
 9174    });
 9175
 9176    cx.update_editor(|editor, window, cx| {
 9177        editor.move_page_up(&MovePageUp::default(), window, cx);
 9178        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9179        {
 9180            assert!(
 9181                menu.selected_item == 0,
 9182                "expected PageUp to select the first item from the context menu"
 9183            );
 9184        } else {
 9185            panic!("expected completion menu to stay open after PageUp");
 9186        }
 9187    });
 9188}
 9189
 9190#[gpui::test]
 9191async fn test_completion_sort(cx: &mut TestAppContext) {
 9192    init_test(cx, |_| {});
 9193    let mut cx = EditorLspTestContext::new_rust(
 9194        lsp::ServerCapabilities {
 9195            completion_provider: Some(lsp::CompletionOptions {
 9196                trigger_characters: Some(vec![".".to_string()]),
 9197                ..Default::default()
 9198            }),
 9199            ..Default::default()
 9200        },
 9201        cx,
 9202    )
 9203    .await;
 9204    cx.lsp
 9205        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9206            Ok(Some(lsp::CompletionResponse::Array(vec![
 9207                lsp::CompletionItem {
 9208                    label: "Range".into(),
 9209                    sort_text: Some("a".into()),
 9210                    ..Default::default()
 9211                },
 9212                lsp::CompletionItem {
 9213                    label: "r".into(),
 9214                    sort_text: Some("b".into()),
 9215                    ..Default::default()
 9216                },
 9217                lsp::CompletionItem {
 9218                    label: "ret".into(),
 9219                    sort_text: Some("c".into()),
 9220                    ..Default::default()
 9221                },
 9222                lsp::CompletionItem {
 9223                    label: "return".into(),
 9224                    sort_text: Some("d".into()),
 9225                    ..Default::default()
 9226                },
 9227                lsp::CompletionItem {
 9228                    label: "slice".into(),
 9229                    sort_text: Some("d".into()),
 9230                    ..Default::default()
 9231                },
 9232            ])))
 9233        });
 9234    cx.set_state("");
 9235    cx.executor().run_until_parked();
 9236    cx.update_editor(|editor, window, cx| {
 9237        editor.show_completions(
 9238            &ShowCompletions {
 9239                trigger: Some("r".into()),
 9240            },
 9241            window,
 9242            cx,
 9243        );
 9244    });
 9245    cx.executor().run_until_parked();
 9246
 9247    cx.update_editor(|editor, _, _| {
 9248        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9249        {
 9250            assert_eq!(
 9251                completion_menu_entries(&menu),
 9252                &["r", "ret", "Range", "return"]
 9253            );
 9254        } else {
 9255            panic!("expected completion menu to be open");
 9256        }
 9257    });
 9258}
 9259
 9260#[gpui::test]
 9261async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9262    init_test(cx, |_| {});
 9263
 9264    let mut cx = EditorLspTestContext::new_rust(
 9265        lsp::ServerCapabilities {
 9266            completion_provider: Some(lsp::CompletionOptions {
 9267                trigger_characters: Some(vec![".".to_string()]),
 9268                resolve_provider: Some(true),
 9269                ..Default::default()
 9270            }),
 9271            ..Default::default()
 9272        },
 9273        cx,
 9274    )
 9275    .await;
 9276
 9277    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9278    cx.simulate_keystroke(".");
 9279    let completion_item = lsp::CompletionItem {
 9280        label: "Some".into(),
 9281        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9282        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9283        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9284            kind: lsp::MarkupKind::Markdown,
 9285            value: "```rust\nSome(2)\n```".to_string(),
 9286        })),
 9287        deprecated: Some(false),
 9288        sort_text: Some("Some".to_string()),
 9289        filter_text: Some("Some".to_string()),
 9290        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9291        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9292            range: lsp::Range {
 9293                start: lsp::Position {
 9294                    line: 0,
 9295                    character: 22,
 9296                },
 9297                end: lsp::Position {
 9298                    line: 0,
 9299                    character: 22,
 9300                },
 9301            },
 9302            new_text: "Some(2)".to_string(),
 9303        })),
 9304        additional_text_edits: Some(vec![lsp::TextEdit {
 9305            range: lsp::Range {
 9306                start: lsp::Position {
 9307                    line: 0,
 9308                    character: 20,
 9309                },
 9310                end: lsp::Position {
 9311                    line: 0,
 9312                    character: 22,
 9313                },
 9314            },
 9315            new_text: "".to_string(),
 9316        }]),
 9317        ..Default::default()
 9318    };
 9319
 9320    let closure_completion_item = completion_item.clone();
 9321    let counter = Arc::new(AtomicUsize::new(0));
 9322    let counter_clone = counter.clone();
 9323    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9324        let task_completion_item = closure_completion_item.clone();
 9325        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9326        async move {
 9327            Ok(Some(lsp::CompletionResponse::Array(vec![
 9328                task_completion_item,
 9329            ])))
 9330        }
 9331    });
 9332
 9333    cx.condition(|editor, _| editor.context_menu_visible())
 9334        .await;
 9335    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9336    assert!(request.next().await.is_some());
 9337    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9338
 9339    cx.simulate_keystroke("S");
 9340    cx.simulate_keystroke("o");
 9341    cx.simulate_keystroke("m");
 9342    cx.condition(|editor, _| editor.context_menu_visible())
 9343        .await;
 9344    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9345    assert!(request.next().await.is_some());
 9346    assert!(request.next().await.is_some());
 9347    assert!(request.next().await.is_some());
 9348    request.close();
 9349    assert!(request.next().await.is_none());
 9350    assert_eq!(
 9351        counter.load(atomic::Ordering::Acquire),
 9352        4,
 9353        "With the completions menu open, only one LSP request should happen per input"
 9354    );
 9355}
 9356
 9357#[gpui::test]
 9358async fn test_toggle_comment(cx: &mut TestAppContext) {
 9359    init_test(cx, |_| {});
 9360    let mut cx = EditorTestContext::new(cx).await;
 9361    let language = Arc::new(Language::new(
 9362        LanguageConfig {
 9363            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9364            ..Default::default()
 9365        },
 9366        Some(tree_sitter_rust::LANGUAGE.into()),
 9367    ));
 9368    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9369
 9370    // If multiple selections intersect a line, the line is only toggled once.
 9371    cx.set_state(indoc! {"
 9372        fn a() {
 9373            «//b();
 9374            ˇ»// «c();
 9375            //ˇ»  d();
 9376        }
 9377    "});
 9378
 9379    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9380
 9381    cx.assert_editor_state(indoc! {"
 9382        fn a() {
 9383            «b();
 9384            c();
 9385            ˇ» d();
 9386        }
 9387    "});
 9388
 9389    // The comment prefix is inserted at the same column for every line in a
 9390    // selection.
 9391    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9392
 9393    cx.assert_editor_state(indoc! {"
 9394        fn a() {
 9395            // «b();
 9396            // c();
 9397            ˇ»//  d();
 9398        }
 9399    "});
 9400
 9401    // If a selection ends at the beginning of a line, that line is not toggled.
 9402    cx.set_selections_state(indoc! {"
 9403        fn a() {
 9404            // b();
 9405            «// c();
 9406        ˇ»    //  d();
 9407        }
 9408    "});
 9409
 9410    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9411
 9412    cx.assert_editor_state(indoc! {"
 9413        fn a() {
 9414            // b();
 9415            «c();
 9416        ˇ»    //  d();
 9417        }
 9418    "});
 9419
 9420    // If a selection span a single line and is empty, the line is toggled.
 9421    cx.set_state(indoc! {"
 9422        fn a() {
 9423            a();
 9424            b();
 9425        ˇ
 9426        }
 9427    "});
 9428
 9429    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9430
 9431    cx.assert_editor_state(indoc! {"
 9432        fn a() {
 9433            a();
 9434            b();
 9435        //•ˇ
 9436        }
 9437    "});
 9438
 9439    // If a selection span multiple lines, empty lines are not toggled.
 9440    cx.set_state(indoc! {"
 9441        fn a() {
 9442            «a();
 9443
 9444            c();ˇ»
 9445        }
 9446    "});
 9447
 9448    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9449
 9450    cx.assert_editor_state(indoc! {"
 9451        fn a() {
 9452            // «a();
 9453
 9454            // c();ˇ»
 9455        }
 9456    "});
 9457
 9458    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9459    cx.set_state(indoc! {"
 9460        fn a() {
 9461            «// a();
 9462            /// b();
 9463            //! c();ˇ»
 9464        }
 9465    "});
 9466
 9467    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9468
 9469    cx.assert_editor_state(indoc! {"
 9470        fn a() {
 9471            «a();
 9472            b();
 9473            c();ˇ»
 9474        }
 9475    "});
 9476}
 9477
 9478#[gpui::test]
 9479async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9480    init_test(cx, |_| {});
 9481    let mut cx = EditorTestContext::new(cx).await;
 9482    let language = Arc::new(Language::new(
 9483        LanguageConfig {
 9484            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9485            ..Default::default()
 9486        },
 9487        Some(tree_sitter_rust::LANGUAGE.into()),
 9488    ));
 9489    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9490
 9491    let toggle_comments = &ToggleComments {
 9492        advance_downwards: false,
 9493        ignore_indent: true,
 9494    };
 9495
 9496    // If multiple selections intersect a line, the line is only toggled once.
 9497    cx.set_state(indoc! {"
 9498        fn a() {
 9499        //    «b();
 9500        //    c();
 9501        //    ˇ» d();
 9502        }
 9503    "});
 9504
 9505    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9506
 9507    cx.assert_editor_state(indoc! {"
 9508        fn a() {
 9509            «b();
 9510            c();
 9511            ˇ» d();
 9512        }
 9513    "});
 9514
 9515    // The comment prefix is inserted at the beginning of each line
 9516    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9517
 9518    cx.assert_editor_state(indoc! {"
 9519        fn a() {
 9520        //    «b();
 9521        //    c();
 9522        //    ˇ» d();
 9523        }
 9524    "});
 9525
 9526    // If a selection ends at the beginning of a line, that line is not toggled.
 9527    cx.set_selections_state(indoc! {"
 9528        fn a() {
 9529        //    b();
 9530        //    «c();
 9531        ˇ»//     d();
 9532        }
 9533    "});
 9534
 9535    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9536
 9537    cx.assert_editor_state(indoc! {"
 9538        fn a() {
 9539        //    b();
 9540            «c();
 9541        ˇ»//     d();
 9542        }
 9543    "});
 9544
 9545    // If a selection span a single line and is empty, the line is toggled.
 9546    cx.set_state(indoc! {"
 9547        fn a() {
 9548            a();
 9549            b();
 9550        ˇ
 9551        }
 9552    "});
 9553
 9554    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9555
 9556    cx.assert_editor_state(indoc! {"
 9557        fn a() {
 9558            a();
 9559            b();
 9560        //ˇ
 9561        }
 9562    "});
 9563
 9564    // If a selection span multiple lines, empty lines are not toggled.
 9565    cx.set_state(indoc! {"
 9566        fn a() {
 9567            «a();
 9568
 9569            c();ˇ»
 9570        }
 9571    "});
 9572
 9573    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9574
 9575    cx.assert_editor_state(indoc! {"
 9576        fn a() {
 9577        //    «a();
 9578
 9579        //    c();ˇ»
 9580        }
 9581    "});
 9582
 9583    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9584    cx.set_state(indoc! {"
 9585        fn a() {
 9586        //    «a();
 9587        ///    b();
 9588        //!    c();ˇ»
 9589        }
 9590    "});
 9591
 9592    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9593
 9594    cx.assert_editor_state(indoc! {"
 9595        fn a() {
 9596            «a();
 9597            b();
 9598            c();ˇ»
 9599        }
 9600    "});
 9601}
 9602
 9603#[gpui::test]
 9604async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9605    init_test(cx, |_| {});
 9606
 9607    let language = Arc::new(Language::new(
 9608        LanguageConfig {
 9609            line_comments: vec!["// ".into()],
 9610            ..Default::default()
 9611        },
 9612        Some(tree_sitter_rust::LANGUAGE.into()),
 9613    ));
 9614
 9615    let mut cx = EditorTestContext::new(cx).await;
 9616
 9617    cx.language_registry().add(language.clone());
 9618    cx.update_buffer(|buffer, cx| {
 9619        buffer.set_language(Some(language), cx);
 9620    });
 9621
 9622    let toggle_comments = &ToggleComments {
 9623        advance_downwards: true,
 9624        ignore_indent: false,
 9625    };
 9626
 9627    // Single cursor on one line -> advance
 9628    // Cursor moves horizontally 3 characters as well on non-blank line
 9629    cx.set_state(indoc!(
 9630        "fn a() {
 9631             ˇdog();
 9632             cat();
 9633        }"
 9634    ));
 9635    cx.update_editor(|editor, window, cx| {
 9636        editor.toggle_comments(toggle_comments, window, cx);
 9637    });
 9638    cx.assert_editor_state(indoc!(
 9639        "fn a() {
 9640             // dog();
 9641             catˇ();
 9642        }"
 9643    ));
 9644
 9645    // Single selection on one line -> don't advance
 9646    cx.set_state(indoc!(
 9647        "fn a() {
 9648             «dog()ˇ»;
 9649             cat();
 9650        }"
 9651    ));
 9652    cx.update_editor(|editor, window, cx| {
 9653        editor.toggle_comments(toggle_comments, window, cx);
 9654    });
 9655    cx.assert_editor_state(indoc!(
 9656        "fn a() {
 9657             // «dog()ˇ»;
 9658             cat();
 9659        }"
 9660    ));
 9661
 9662    // Multiple cursors on one line -> advance
 9663    cx.set_state(indoc!(
 9664        "fn a() {
 9665             ˇdˇog();
 9666             cat();
 9667        }"
 9668    ));
 9669    cx.update_editor(|editor, window, cx| {
 9670        editor.toggle_comments(toggle_comments, window, cx);
 9671    });
 9672    cx.assert_editor_state(indoc!(
 9673        "fn a() {
 9674             // dog();
 9675             catˇ(ˇ);
 9676        }"
 9677    ));
 9678
 9679    // Multiple cursors on one line, with selection -> don't advance
 9680    cx.set_state(indoc!(
 9681        "fn a() {
 9682             ˇdˇog«()ˇ»;
 9683             cat();
 9684        }"
 9685    ));
 9686    cx.update_editor(|editor, window, cx| {
 9687        editor.toggle_comments(toggle_comments, window, cx);
 9688    });
 9689    cx.assert_editor_state(indoc!(
 9690        "fn a() {
 9691             // ˇdˇog«()ˇ»;
 9692             cat();
 9693        }"
 9694    ));
 9695
 9696    // Single cursor on one line -> advance
 9697    // Cursor moves to column 0 on blank line
 9698    cx.set_state(indoc!(
 9699        "fn a() {
 9700             ˇdog();
 9701
 9702             cat();
 9703        }"
 9704    ));
 9705    cx.update_editor(|editor, window, cx| {
 9706        editor.toggle_comments(toggle_comments, window, cx);
 9707    });
 9708    cx.assert_editor_state(indoc!(
 9709        "fn a() {
 9710             // dog();
 9711        ˇ
 9712             cat();
 9713        }"
 9714    ));
 9715
 9716    // Single cursor on one line -> advance
 9717    // Cursor starts and ends at column 0
 9718    cx.set_state(indoc!(
 9719        "fn a() {
 9720         ˇ    dog();
 9721             cat();
 9722        }"
 9723    ));
 9724    cx.update_editor(|editor, window, cx| {
 9725        editor.toggle_comments(toggle_comments, window, cx);
 9726    });
 9727    cx.assert_editor_state(indoc!(
 9728        "fn a() {
 9729             // dog();
 9730         ˇ    cat();
 9731        }"
 9732    ));
 9733}
 9734
 9735#[gpui::test]
 9736async fn test_toggle_block_comment(cx: &mut TestAppContext) {
 9737    init_test(cx, |_| {});
 9738
 9739    let mut cx = EditorTestContext::new(cx).await;
 9740
 9741    let html_language = Arc::new(
 9742        Language::new(
 9743            LanguageConfig {
 9744                name: "HTML".into(),
 9745                block_comment: Some(("<!-- ".into(), " -->".into())),
 9746                ..Default::default()
 9747            },
 9748            Some(tree_sitter_html::LANGUAGE.into()),
 9749        )
 9750        .with_injection_query(
 9751            r#"
 9752            (script_element
 9753                (raw_text) @injection.content
 9754                (#set! injection.language "javascript"))
 9755            "#,
 9756        )
 9757        .unwrap(),
 9758    );
 9759
 9760    let javascript_language = Arc::new(Language::new(
 9761        LanguageConfig {
 9762            name: "JavaScript".into(),
 9763            line_comments: vec!["// ".into()],
 9764            ..Default::default()
 9765        },
 9766        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9767    ));
 9768
 9769    cx.language_registry().add(html_language.clone());
 9770    cx.language_registry().add(javascript_language.clone());
 9771    cx.update_buffer(|buffer, cx| {
 9772        buffer.set_language(Some(html_language), cx);
 9773    });
 9774
 9775    // Toggle comments for empty selections
 9776    cx.set_state(
 9777        &r#"
 9778            <p>A</p>ˇ
 9779            <p>B</p>ˇ
 9780            <p>C</p>ˇ
 9781        "#
 9782        .unindent(),
 9783    );
 9784    cx.update_editor(|editor, window, cx| {
 9785        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9786    });
 9787    cx.assert_editor_state(
 9788        &r#"
 9789            <!-- <p>A</p>ˇ -->
 9790            <!-- <p>B</p>ˇ -->
 9791            <!-- <p>C</p>ˇ -->
 9792        "#
 9793        .unindent(),
 9794    );
 9795    cx.update_editor(|editor, window, cx| {
 9796        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9797    });
 9798    cx.assert_editor_state(
 9799        &r#"
 9800            <p>A</p>ˇ
 9801            <p>B</p>ˇ
 9802            <p>C</p>ˇ
 9803        "#
 9804        .unindent(),
 9805    );
 9806
 9807    // Toggle comments for mixture of empty and non-empty selections, where
 9808    // multiple selections occupy a given line.
 9809    cx.set_state(
 9810        &r#"
 9811            <p>A«</p>
 9812            <p>ˇ»B</p>ˇ
 9813            <p>C«</p>
 9814            <p>ˇ»D</p>ˇ
 9815        "#
 9816        .unindent(),
 9817    );
 9818
 9819    cx.update_editor(|editor, window, cx| {
 9820        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9821    });
 9822    cx.assert_editor_state(
 9823        &r#"
 9824            <!-- <p>A«</p>
 9825            <p>ˇ»B</p>ˇ -->
 9826            <!-- <p>C«</p>
 9827            <p>ˇ»D</p>ˇ -->
 9828        "#
 9829        .unindent(),
 9830    );
 9831    cx.update_editor(|editor, window, cx| {
 9832        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9833    });
 9834    cx.assert_editor_state(
 9835        &r#"
 9836            <p>A«</p>
 9837            <p>ˇ»B</p>ˇ
 9838            <p>C«</p>
 9839            <p>ˇ»D</p>ˇ
 9840        "#
 9841        .unindent(),
 9842    );
 9843
 9844    // Toggle comments when different languages are active for different
 9845    // selections.
 9846    cx.set_state(
 9847        &r#"
 9848            ˇ<script>
 9849                ˇvar x = new Y();
 9850            ˇ</script>
 9851        "#
 9852        .unindent(),
 9853    );
 9854    cx.executor().run_until_parked();
 9855    cx.update_editor(|editor, window, cx| {
 9856        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9857    });
 9858    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9859    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9860    cx.assert_editor_state(
 9861        &r#"
 9862            <!-- ˇ<script> -->
 9863                // ˇvar x = new Y();
 9864            <!-- ˇ</script> -->
 9865        "#
 9866        .unindent(),
 9867    );
 9868}
 9869
 9870#[gpui::test]
 9871fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9872    init_test(cx, |_| {});
 9873
 9874    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9875    let multibuffer = cx.new(|cx| {
 9876        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9877        multibuffer.push_excerpts(
 9878            buffer.clone(),
 9879            [
 9880                ExcerptRange {
 9881                    context: Point::new(0, 0)..Point::new(0, 4),
 9882                    primary: None,
 9883                },
 9884                ExcerptRange {
 9885                    context: Point::new(1, 0)..Point::new(1, 4),
 9886                    primary: None,
 9887                },
 9888            ],
 9889            cx,
 9890        );
 9891        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9892        multibuffer
 9893    });
 9894
 9895    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9896    editor.update_in(cx, |editor, window, cx| {
 9897        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9898        editor.change_selections(None, window, cx, |s| {
 9899            s.select_ranges([
 9900                Point::new(0, 0)..Point::new(0, 0),
 9901                Point::new(1, 0)..Point::new(1, 0),
 9902            ])
 9903        });
 9904
 9905        editor.handle_input("X", window, cx);
 9906        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9907        assert_eq!(
 9908            editor.selections.ranges(cx),
 9909            [
 9910                Point::new(0, 1)..Point::new(0, 1),
 9911                Point::new(1, 1)..Point::new(1, 1),
 9912            ]
 9913        );
 9914
 9915        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9916        editor.change_selections(None, window, cx, |s| {
 9917            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9918        });
 9919        editor.backspace(&Default::default(), window, cx);
 9920        assert_eq!(editor.text(cx), "Xa\nbbb");
 9921        assert_eq!(
 9922            editor.selections.ranges(cx),
 9923            [Point::new(1, 0)..Point::new(1, 0)]
 9924        );
 9925
 9926        editor.change_selections(None, window, cx, |s| {
 9927            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9928        });
 9929        editor.backspace(&Default::default(), window, cx);
 9930        assert_eq!(editor.text(cx), "X\nbb");
 9931        assert_eq!(
 9932            editor.selections.ranges(cx),
 9933            [Point::new(0, 1)..Point::new(0, 1)]
 9934        );
 9935    });
 9936}
 9937
 9938#[gpui::test]
 9939fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9940    init_test(cx, |_| {});
 9941
 9942    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9943    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9944        indoc! {"
 9945            [aaaa
 9946            (bbbb]
 9947            cccc)",
 9948        },
 9949        markers.clone(),
 9950    );
 9951    let excerpt_ranges = markers.into_iter().map(|marker| {
 9952        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9953        ExcerptRange {
 9954            context,
 9955            primary: None,
 9956        }
 9957    });
 9958    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9959    let multibuffer = cx.new(|cx| {
 9960        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9961        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9962        multibuffer
 9963    });
 9964
 9965    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9966    editor.update_in(cx, |editor, window, cx| {
 9967        let (expected_text, selection_ranges) = marked_text_ranges(
 9968            indoc! {"
 9969                aaaa
 9970                bˇbbb
 9971                bˇbbˇb
 9972                cccc"
 9973            },
 9974            true,
 9975        );
 9976        assert_eq!(editor.text(cx), expected_text);
 9977        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9978
 9979        editor.handle_input("X", window, cx);
 9980
 9981        let (expected_text, expected_selections) = marked_text_ranges(
 9982            indoc! {"
 9983                aaaa
 9984                bXˇbbXb
 9985                bXˇbbXˇb
 9986                cccc"
 9987            },
 9988            false,
 9989        );
 9990        assert_eq!(editor.text(cx), expected_text);
 9991        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9992
 9993        editor.newline(&Newline, window, cx);
 9994        let (expected_text, expected_selections) = marked_text_ranges(
 9995            indoc! {"
 9996                aaaa
 9997                bX
 9998                ˇbbX
 9999                b
10000                bX
10001                ˇbbX
10002                ˇb
10003                cccc"
10004            },
10005            false,
10006        );
10007        assert_eq!(editor.text(cx), expected_text);
10008        assert_eq!(editor.selections.ranges(cx), expected_selections);
10009    });
10010}
10011
10012#[gpui::test]
10013fn test_refresh_selections(cx: &mut TestAppContext) {
10014    init_test(cx, |_| {});
10015
10016    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10017    let mut excerpt1_id = None;
10018    let multibuffer = cx.new(|cx| {
10019        let mut multibuffer = MultiBuffer::new(ReadWrite);
10020        excerpt1_id = multibuffer
10021            .push_excerpts(
10022                buffer.clone(),
10023                [
10024                    ExcerptRange {
10025                        context: Point::new(0, 0)..Point::new(1, 4),
10026                        primary: None,
10027                    },
10028                    ExcerptRange {
10029                        context: Point::new(1, 0)..Point::new(2, 4),
10030                        primary: None,
10031                    },
10032                ],
10033                cx,
10034            )
10035            .into_iter()
10036            .next();
10037        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10038        multibuffer
10039    });
10040
10041    let editor = cx.add_window(|window, cx| {
10042        let mut editor = build_editor(multibuffer.clone(), window, cx);
10043        let snapshot = editor.snapshot(window, cx);
10044        editor.change_selections(None, window, cx, |s| {
10045            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10046        });
10047        editor.begin_selection(
10048            Point::new(2, 1).to_display_point(&snapshot),
10049            true,
10050            1,
10051            window,
10052            cx,
10053        );
10054        assert_eq!(
10055            editor.selections.ranges(cx),
10056            [
10057                Point::new(1, 3)..Point::new(1, 3),
10058                Point::new(2, 1)..Point::new(2, 1),
10059            ]
10060        );
10061        editor
10062    });
10063
10064    // Refreshing selections is a no-op when excerpts haven't changed.
10065    _ = editor.update(cx, |editor, window, cx| {
10066        editor.change_selections(None, window, cx, |s| s.refresh());
10067        assert_eq!(
10068            editor.selections.ranges(cx),
10069            [
10070                Point::new(1, 3)..Point::new(1, 3),
10071                Point::new(2, 1)..Point::new(2, 1),
10072            ]
10073        );
10074    });
10075
10076    multibuffer.update(cx, |multibuffer, cx| {
10077        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10078    });
10079    _ = editor.update(cx, |editor, window, cx| {
10080        // Removing an excerpt causes the first selection to become degenerate.
10081        assert_eq!(
10082            editor.selections.ranges(cx),
10083            [
10084                Point::new(0, 0)..Point::new(0, 0),
10085                Point::new(0, 1)..Point::new(0, 1)
10086            ]
10087        );
10088
10089        // Refreshing selections will relocate the first selection to the original buffer
10090        // location.
10091        editor.change_selections(None, window, cx, |s| s.refresh());
10092        assert_eq!(
10093            editor.selections.ranges(cx),
10094            [
10095                Point::new(0, 1)..Point::new(0, 1),
10096                Point::new(0, 3)..Point::new(0, 3)
10097            ]
10098        );
10099        assert!(editor.selections.pending_anchor().is_some());
10100    });
10101}
10102
10103#[gpui::test]
10104fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10105    init_test(cx, |_| {});
10106
10107    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10108    let mut excerpt1_id = None;
10109    let multibuffer = cx.new(|cx| {
10110        let mut multibuffer = MultiBuffer::new(ReadWrite);
10111        excerpt1_id = multibuffer
10112            .push_excerpts(
10113                buffer.clone(),
10114                [
10115                    ExcerptRange {
10116                        context: Point::new(0, 0)..Point::new(1, 4),
10117                        primary: None,
10118                    },
10119                    ExcerptRange {
10120                        context: Point::new(1, 0)..Point::new(2, 4),
10121                        primary: None,
10122                    },
10123                ],
10124                cx,
10125            )
10126            .into_iter()
10127            .next();
10128        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10129        multibuffer
10130    });
10131
10132    let editor = cx.add_window(|window, cx| {
10133        let mut editor = build_editor(multibuffer.clone(), window, cx);
10134        let snapshot = editor.snapshot(window, cx);
10135        editor.begin_selection(
10136            Point::new(1, 3).to_display_point(&snapshot),
10137            false,
10138            1,
10139            window,
10140            cx,
10141        );
10142        assert_eq!(
10143            editor.selections.ranges(cx),
10144            [Point::new(1, 3)..Point::new(1, 3)]
10145        );
10146        editor
10147    });
10148
10149    multibuffer.update(cx, |multibuffer, cx| {
10150        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10151    });
10152    _ = editor.update(cx, |editor, window, cx| {
10153        assert_eq!(
10154            editor.selections.ranges(cx),
10155            [Point::new(0, 0)..Point::new(0, 0)]
10156        );
10157
10158        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10159        editor.change_selections(None, window, cx, |s| s.refresh());
10160        assert_eq!(
10161            editor.selections.ranges(cx),
10162            [Point::new(0, 3)..Point::new(0, 3)]
10163        );
10164        assert!(editor.selections.pending_anchor().is_some());
10165    });
10166}
10167
10168#[gpui::test]
10169async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10170    init_test(cx, |_| {});
10171
10172    let language = Arc::new(
10173        Language::new(
10174            LanguageConfig {
10175                brackets: BracketPairConfig {
10176                    pairs: vec![
10177                        BracketPair {
10178                            start: "{".to_string(),
10179                            end: "}".to_string(),
10180                            close: true,
10181                            surround: true,
10182                            newline: true,
10183                        },
10184                        BracketPair {
10185                            start: "/* ".to_string(),
10186                            end: " */".to_string(),
10187                            close: true,
10188                            surround: true,
10189                            newline: true,
10190                        },
10191                    ],
10192                    ..Default::default()
10193                },
10194                ..Default::default()
10195            },
10196            Some(tree_sitter_rust::LANGUAGE.into()),
10197        )
10198        .with_indents_query("")
10199        .unwrap(),
10200    );
10201
10202    let text = concat!(
10203        "{   }\n",     //
10204        "  x\n",       //
10205        "  /*   */\n", //
10206        "x\n",         //
10207        "{{} }\n",     //
10208    );
10209
10210    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10211    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10212    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10213    editor
10214        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10215        .await;
10216
10217    editor.update_in(cx, |editor, window, cx| {
10218        editor.change_selections(None, window, cx, |s| {
10219            s.select_display_ranges([
10220                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10221                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10222                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10223            ])
10224        });
10225        editor.newline(&Newline, window, cx);
10226
10227        assert_eq!(
10228            editor.buffer().read(cx).read(cx).text(),
10229            concat!(
10230                "{ \n",    // Suppress rustfmt
10231                "\n",      //
10232                "}\n",     //
10233                "  x\n",   //
10234                "  /* \n", //
10235                "  \n",    //
10236                "  */\n",  //
10237                "x\n",     //
10238                "{{} \n",  //
10239                "}\n",     //
10240            )
10241        );
10242    });
10243}
10244
10245#[gpui::test]
10246fn test_highlighted_ranges(cx: &mut TestAppContext) {
10247    init_test(cx, |_| {});
10248
10249    let editor = cx.add_window(|window, cx| {
10250        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10251        build_editor(buffer.clone(), window, cx)
10252    });
10253
10254    _ = editor.update(cx, |editor, window, cx| {
10255        struct Type1;
10256        struct Type2;
10257
10258        let buffer = editor.buffer.read(cx).snapshot(cx);
10259
10260        let anchor_range =
10261            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10262
10263        editor.highlight_background::<Type1>(
10264            &[
10265                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10266                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10267                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10268                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10269            ],
10270            |_| Hsla::red(),
10271            cx,
10272        );
10273        editor.highlight_background::<Type2>(
10274            &[
10275                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10276                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10277                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10278                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10279            ],
10280            |_| Hsla::green(),
10281            cx,
10282        );
10283
10284        let snapshot = editor.snapshot(window, cx);
10285        let mut highlighted_ranges = editor.background_highlights_in_range(
10286            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10287            &snapshot,
10288            cx.theme().colors(),
10289        );
10290        // Enforce a consistent ordering based on color without relying on the ordering of the
10291        // highlight's `TypeId` which is non-executor.
10292        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10293        assert_eq!(
10294            highlighted_ranges,
10295            &[
10296                (
10297                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10298                    Hsla::red(),
10299                ),
10300                (
10301                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10302                    Hsla::red(),
10303                ),
10304                (
10305                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10306                    Hsla::green(),
10307                ),
10308                (
10309                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10310                    Hsla::green(),
10311                ),
10312            ]
10313        );
10314        assert_eq!(
10315            editor.background_highlights_in_range(
10316                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10317                &snapshot,
10318                cx.theme().colors(),
10319            ),
10320            &[(
10321                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10322                Hsla::red(),
10323            )]
10324        );
10325    });
10326}
10327
10328#[gpui::test]
10329async fn test_following(cx: &mut TestAppContext) {
10330    init_test(cx, |_| {});
10331
10332    let fs = FakeFs::new(cx.executor());
10333    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10334
10335    let buffer = project.update(cx, |project, cx| {
10336        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10337        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10338    });
10339    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10340    let follower = cx.update(|cx| {
10341        cx.open_window(
10342            WindowOptions {
10343                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10344                    gpui::Point::new(px(0.), px(0.)),
10345                    gpui::Point::new(px(10.), px(80.)),
10346                ))),
10347                ..Default::default()
10348            },
10349            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10350        )
10351        .unwrap()
10352    });
10353
10354    let is_still_following = Rc::new(RefCell::new(true));
10355    let follower_edit_event_count = Rc::new(RefCell::new(0));
10356    let pending_update = Rc::new(RefCell::new(None));
10357    let leader_entity = leader.root(cx).unwrap();
10358    let follower_entity = follower.root(cx).unwrap();
10359    _ = follower.update(cx, {
10360        let update = pending_update.clone();
10361        let is_still_following = is_still_following.clone();
10362        let follower_edit_event_count = follower_edit_event_count.clone();
10363        |_, window, cx| {
10364            cx.subscribe_in(
10365                &leader_entity,
10366                window,
10367                move |_, leader, event, window, cx| {
10368                    leader.read(cx).add_event_to_update_proto(
10369                        event,
10370                        &mut update.borrow_mut(),
10371                        window,
10372                        cx,
10373                    );
10374                },
10375            )
10376            .detach();
10377
10378            cx.subscribe_in(
10379                &follower_entity,
10380                window,
10381                move |_, _, event: &EditorEvent, _window, _cx| {
10382                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10383                        *is_still_following.borrow_mut() = false;
10384                    }
10385
10386                    if let EditorEvent::BufferEdited = event {
10387                        *follower_edit_event_count.borrow_mut() += 1;
10388                    }
10389                },
10390            )
10391            .detach();
10392        }
10393    });
10394
10395    // Update the selections only
10396    _ = leader.update(cx, |leader, window, cx| {
10397        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10398    });
10399    follower
10400        .update(cx, |follower, window, cx| {
10401            follower.apply_update_proto(
10402                &project,
10403                pending_update.borrow_mut().take().unwrap(),
10404                window,
10405                cx,
10406            )
10407        })
10408        .unwrap()
10409        .await
10410        .unwrap();
10411    _ = follower.update(cx, |follower, _, cx| {
10412        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10413    });
10414    assert!(*is_still_following.borrow());
10415    assert_eq!(*follower_edit_event_count.borrow(), 0);
10416
10417    // Update the scroll position only
10418    _ = leader.update(cx, |leader, window, cx| {
10419        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10420    });
10421    follower
10422        .update(cx, |follower, window, cx| {
10423            follower.apply_update_proto(
10424                &project,
10425                pending_update.borrow_mut().take().unwrap(),
10426                window,
10427                cx,
10428            )
10429        })
10430        .unwrap()
10431        .await
10432        .unwrap();
10433    assert_eq!(
10434        follower
10435            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10436            .unwrap(),
10437        gpui::Point::new(1.5, 3.5)
10438    );
10439    assert!(*is_still_following.borrow());
10440    assert_eq!(*follower_edit_event_count.borrow(), 0);
10441
10442    // Update the selections and scroll position. The follower's scroll position is updated
10443    // via autoscroll, not via the leader's exact scroll position.
10444    _ = leader.update(cx, |leader, window, cx| {
10445        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10446        leader.request_autoscroll(Autoscroll::newest(), cx);
10447        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10448    });
10449    follower
10450        .update(cx, |follower, window, cx| {
10451            follower.apply_update_proto(
10452                &project,
10453                pending_update.borrow_mut().take().unwrap(),
10454                window,
10455                cx,
10456            )
10457        })
10458        .unwrap()
10459        .await
10460        .unwrap();
10461    _ = follower.update(cx, |follower, _, cx| {
10462        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10463        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10464    });
10465    assert!(*is_still_following.borrow());
10466
10467    // Creating a pending selection that precedes another selection
10468    _ = leader.update(cx, |leader, window, cx| {
10469        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10470        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10471    });
10472    follower
10473        .update(cx, |follower, window, cx| {
10474            follower.apply_update_proto(
10475                &project,
10476                pending_update.borrow_mut().take().unwrap(),
10477                window,
10478                cx,
10479            )
10480        })
10481        .unwrap()
10482        .await
10483        .unwrap();
10484    _ = follower.update(cx, |follower, _, cx| {
10485        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10486    });
10487    assert!(*is_still_following.borrow());
10488
10489    // Extend the pending selection so that it surrounds another selection
10490    _ = leader.update(cx, |leader, window, cx| {
10491        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10492    });
10493    follower
10494        .update(cx, |follower, window, cx| {
10495            follower.apply_update_proto(
10496                &project,
10497                pending_update.borrow_mut().take().unwrap(),
10498                window,
10499                cx,
10500            )
10501        })
10502        .unwrap()
10503        .await
10504        .unwrap();
10505    _ = follower.update(cx, |follower, _, cx| {
10506        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10507    });
10508
10509    // Scrolling locally breaks the follow
10510    _ = follower.update(cx, |follower, window, cx| {
10511        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10512        follower.set_scroll_anchor(
10513            ScrollAnchor {
10514                anchor: top_anchor,
10515                offset: gpui::Point::new(0.0, 0.5),
10516            },
10517            window,
10518            cx,
10519        );
10520    });
10521    assert!(!(*is_still_following.borrow()));
10522}
10523
10524#[gpui::test]
10525async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10526    init_test(cx, |_| {});
10527
10528    let fs = FakeFs::new(cx.executor());
10529    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10530    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10531    let pane = workspace
10532        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10533        .unwrap();
10534
10535    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10536
10537    let leader = pane.update_in(cx, |_, window, cx| {
10538        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10539        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10540    });
10541
10542    // Start following the editor when it has no excerpts.
10543    let mut state_message =
10544        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10545    let workspace_entity = workspace.root(cx).unwrap();
10546    let follower_1 = cx
10547        .update_window(*workspace.deref(), |_, window, cx| {
10548            Editor::from_state_proto(
10549                workspace_entity,
10550                ViewId {
10551                    creator: Default::default(),
10552                    id: 0,
10553                },
10554                &mut state_message,
10555                window,
10556                cx,
10557            )
10558        })
10559        .unwrap()
10560        .unwrap()
10561        .await
10562        .unwrap();
10563
10564    let update_message = Rc::new(RefCell::new(None));
10565    follower_1.update_in(cx, {
10566        let update = update_message.clone();
10567        |_, window, cx| {
10568            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10569                leader.read(cx).add_event_to_update_proto(
10570                    event,
10571                    &mut update.borrow_mut(),
10572                    window,
10573                    cx,
10574                );
10575            })
10576            .detach();
10577        }
10578    });
10579
10580    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10581        (
10582            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10583            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10584        )
10585    });
10586
10587    // Insert some excerpts.
10588    leader.update(cx, |leader, cx| {
10589        leader.buffer.update(cx, |multibuffer, cx| {
10590            let excerpt_ids = multibuffer.push_excerpts(
10591                buffer_1.clone(),
10592                [
10593                    ExcerptRange {
10594                        context: 1..6,
10595                        primary: None,
10596                    },
10597                    ExcerptRange {
10598                        context: 12..15,
10599                        primary: None,
10600                    },
10601                    ExcerptRange {
10602                        context: 0..3,
10603                        primary: None,
10604                    },
10605                ],
10606                cx,
10607            );
10608            multibuffer.insert_excerpts_after(
10609                excerpt_ids[0],
10610                buffer_2.clone(),
10611                [
10612                    ExcerptRange {
10613                        context: 8..12,
10614                        primary: None,
10615                    },
10616                    ExcerptRange {
10617                        context: 0..6,
10618                        primary: None,
10619                    },
10620                ],
10621                cx,
10622            );
10623        });
10624    });
10625
10626    // Apply the update of adding the excerpts.
10627    follower_1
10628        .update_in(cx, |follower, window, cx| {
10629            follower.apply_update_proto(
10630                &project,
10631                update_message.borrow().clone().unwrap(),
10632                window,
10633                cx,
10634            )
10635        })
10636        .await
10637        .unwrap();
10638    assert_eq!(
10639        follower_1.update(cx, |editor, cx| editor.text(cx)),
10640        leader.update(cx, |editor, cx| editor.text(cx))
10641    );
10642    update_message.borrow_mut().take();
10643
10644    // Start following separately after it already has excerpts.
10645    let mut state_message =
10646        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10647    let workspace_entity = workspace.root(cx).unwrap();
10648    let follower_2 = cx
10649        .update_window(*workspace.deref(), |_, window, cx| {
10650            Editor::from_state_proto(
10651                workspace_entity,
10652                ViewId {
10653                    creator: Default::default(),
10654                    id: 0,
10655                },
10656                &mut state_message,
10657                window,
10658                cx,
10659            )
10660        })
10661        .unwrap()
10662        .unwrap()
10663        .await
10664        .unwrap();
10665    assert_eq!(
10666        follower_2.update(cx, |editor, cx| editor.text(cx)),
10667        leader.update(cx, |editor, cx| editor.text(cx))
10668    );
10669
10670    // Remove some excerpts.
10671    leader.update(cx, |leader, cx| {
10672        leader.buffer.update(cx, |multibuffer, cx| {
10673            let excerpt_ids = multibuffer.excerpt_ids();
10674            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10675            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10676        });
10677    });
10678
10679    // Apply the update of removing the excerpts.
10680    follower_1
10681        .update_in(cx, |follower, window, cx| {
10682            follower.apply_update_proto(
10683                &project,
10684                update_message.borrow().clone().unwrap(),
10685                window,
10686                cx,
10687            )
10688        })
10689        .await
10690        .unwrap();
10691    follower_2
10692        .update_in(cx, |follower, window, cx| {
10693            follower.apply_update_proto(
10694                &project,
10695                update_message.borrow().clone().unwrap(),
10696                window,
10697                cx,
10698            )
10699        })
10700        .await
10701        .unwrap();
10702    update_message.borrow_mut().take();
10703    assert_eq!(
10704        follower_1.update(cx, |editor, cx| editor.text(cx)),
10705        leader.update(cx, |editor, cx| editor.text(cx))
10706    );
10707}
10708
10709#[gpui::test]
10710async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
10711    init_test(cx, |_| {});
10712
10713    let mut cx = EditorTestContext::new(cx).await;
10714    let lsp_store =
10715        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10716
10717    cx.set_state(indoc! {"
10718        ˇfn func(abc def: i32) -> u32 {
10719        }
10720    "});
10721
10722    cx.update(|_, cx| {
10723        lsp_store.update(cx, |lsp_store, cx| {
10724            lsp_store
10725                .update_diagnostics(
10726                    LanguageServerId(0),
10727                    lsp::PublishDiagnosticsParams {
10728                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10729                        version: None,
10730                        diagnostics: vec![
10731                            lsp::Diagnostic {
10732                                range: lsp::Range::new(
10733                                    lsp::Position::new(0, 11),
10734                                    lsp::Position::new(0, 12),
10735                                ),
10736                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10737                                ..Default::default()
10738                            },
10739                            lsp::Diagnostic {
10740                                range: lsp::Range::new(
10741                                    lsp::Position::new(0, 12),
10742                                    lsp::Position::new(0, 15),
10743                                ),
10744                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10745                                ..Default::default()
10746                            },
10747                            lsp::Diagnostic {
10748                                range: lsp::Range::new(
10749                                    lsp::Position::new(0, 25),
10750                                    lsp::Position::new(0, 28),
10751                                ),
10752                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10753                                ..Default::default()
10754                            },
10755                        ],
10756                    },
10757                    &[],
10758                    cx,
10759                )
10760                .unwrap()
10761        });
10762    });
10763
10764    executor.run_until_parked();
10765
10766    cx.update_editor(|editor, window, cx| {
10767        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10768    });
10769
10770    cx.assert_editor_state(indoc! {"
10771        fn func(abc def: i32) -> ˇu32 {
10772        }
10773    "});
10774
10775    cx.update_editor(|editor, window, cx| {
10776        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10777    });
10778
10779    cx.assert_editor_state(indoc! {"
10780        fn func(abc ˇdef: i32) -> u32 {
10781        }
10782    "});
10783
10784    cx.update_editor(|editor, window, cx| {
10785        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10786    });
10787
10788    cx.assert_editor_state(indoc! {"
10789        fn func(abcˇ def: i32) -> u32 {
10790        }
10791    "});
10792
10793    cx.update_editor(|editor, window, cx| {
10794        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10795    });
10796
10797    cx.assert_editor_state(indoc! {"
10798        fn func(abc def: i32) -> ˇu32 {
10799        }
10800    "});
10801}
10802
10803#[gpui::test]
10804async fn cycle_through_same_place_diagnostics(
10805    executor: BackgroundExecutor,
10806    cx: &mut TestAppContext,
10807) {
10808    init_test(cx, |_| {});
10809
10810    let mut cx = EditorTestContext::new(cx).await;
10811    let lsp_store =
10812        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10813
10814    cx.set_state(indoc! {"
10815        ˇfn func(abc def: i32) -> u32 {
10816        }
10817    "});
10818
10819    cx.update(|_, cx| {
10820        lsp_store.update(cx, |lsp_store, cx| {
10821            lsp_store
10822                .update_diagnostics(
10823                    LanguageServerId(0),
10824                    lsp::PublishDiagnosticsParams {
10825                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10826                        version: None,
10827                        diagnostics: vec![
10828                            lsp::Diagnostic {
10829                                range: lsp::Range::new(
10830                                    lsp::Position::new(0, 11),
10831                                    lsp::Position::new(0, 12),
10832                                ),
10833                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10834                                ..Default::default()
10835                            },
10836                            lsp::Diagnostic {
10837                                range: lsp::Range::new(
10838                                    lsp::Position::new(0, 12),
10839                                    lsp::Position::new(0, 15),
10840                                ),
10841                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10842                                ..Default::default()
10843                            },
10844                            lsp::Diagnostic {
10845                                range: lsp::Range::new(
10846                                    lsp::Position::new(0, 12),
10847                                    lsp::Position::new(0, 15),
10848                                ),
10849                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10850                                ..Default::default()
10851                            },
10852                            lsp::Diagnostic {
10853                                range: lsp::Range::new(
10854                                    lsp::Position::new(0, 25),
10855                                    lsp::Position::new(0, 28),
10856                                ),
10857                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10858                                ..Default::default()
10859                            },
10860                        ],
10861                    },
10862                    &[],
10863                    cx,
10864                )
10865                .unwrap()
10866        });
10867    });
10868    executor.run_until_parked();
10869
10870    //// Backward
10871
10872    // Fourth diagnostic
10873    cx.update_editor(|editor, window, cx| {
10874        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10875    });
10876    cx.assert_editor_state(indoc! {"
10877        fn func(abc def: i32) -> ˇu32 {
10878        }
10879    "});
10880
10881    // Third diagnostic
10882    cx.update_editor(|editor, window, cx| {
10883        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10884    });
10885    cx.assert_editor_state(indoc! {"
10886        fn func(abc ˇdef: i32) -> u32 {
10887        }
10888    "});
10889
10890    // Second diagnostic, same place
10891    cx.update_editor(|editor, window, cx| {
10892        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10893    });
10894    cx.assert_editor_state(indoc! {"
10895        fn func(abc ˇdef: i32) -> u32 {
10896        }
10897    "});
10898
10899    // First diagnostic
10900    cx.update_editor(|editor, window, cx| {
10901        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10902    });
10903    cx.assert_editor_state(indoc! {"
10904        fn func(abcˇ def: i32) -> u32 {
10905        }
10906    "});
10907
10908    // Wrapped over, fourth diagnostic
10909    cx.update_editor(|editor, window, cx| {
10910        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10911    });
10912    cx.assert_editor_state(indoc! {"
10913        fn func(abc def: i32) -> ˇu32 {
10914        }
10915    "});
10916
10917    cx.update_editor(|editor, window, cx| {
10918        editor.move_to_beginning(&MoveToBeginning, window, cx);
10919    });
10920    cx.assert_editor_state(indoc! {"
10921        ˇfn func(abc def: i32) -> u32 {
10922        }
10923    "});
10924
10925    //// Forward
10926
10927    // First diagnostic
10928    cx.update_editor(|editor, window, cx| {
10929        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10930    });
10931    cx.assert_editor_state(indoc! {"
10932        fn func(abcˇ def: i32) -> u32 {
10933        }
10934    "});
10935
10936    // Second diagnostic
10937    cx.update_editor(|editor, window, cx| {
10938        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10939    });
10940    cx.assert_editor_state(indoc! {"
10941        fn func(abc ˇdef: i32) -> u32 {
10942        }
10943    "});
10944
10945    // Third diagnostic, same place
10946    cx.update_editor(|editor, window, cx| {
10947        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10948    });
10949    cx.assert_editor_state(indoc! {"
10950        fn func(abc ˇdef: i32) -> u32 {
10951        }
10952    "});
10953
10954    // Fourth diagnostic
10955    cx.update_editor(|editor, window, cx| {
10956        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10957    });
10958    cx.assert_editor_state(indoc! {"
10959        fn func(abc def: i32) -> ˇu32 {
10960        }
10961    "});
10962
10963    // Wrapped around, first diagnostic
10964    cx.update_editor(|editor, window, cx| {
10965        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10966    });
10967    cx.assert_editor_state(indoc! {"
10968        fn func(abcˇ def: i32) -> u32 {
10969        }
10970    "});
10971}
10972
10973#[gpui::test]
10974async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10975    init_test(cx, |_| {});
10976
10977    let mut cx = EditorTestContext::new(cx).await;
10978
10979    cx.set_state(indoc! {"
10980        fn func(abˇc def: i32) -> u32 {
10981        }
10982    "});
10983    let lsp_store =
10984        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10985
10986    cx.update(|_, cx| {
10987        lsp_store.update(cx, |lsp_store, cx| {
10988            lsp_store.update_diagnostics(
10989                LanguageServerId(0),
10990                lsp::PublishDiagnosticsParams {
10991                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10992                    version: None,
10993                    diagnostics: vec![lsp::Diagnostic {
10994                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10995                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10996                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10997                        ..Default::default()
10998                    }],
10999                },
11000                &[],
11001                cx,
11002            )
11003        })
11004    }).unwrap();
11005    cx.run_until_parked();
11006    cx.update_editor(|editor, window, cx| {
11007        hover_popover::hover(editor, &Default::default(), window, cx)
11008    });
11009    cx.run_until_parked();
11010    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11011}
11012
11013#[gpui::test]
11014async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11015    init_test(cx, |_| {});
11016
11017    let mut cx = EditorTestContext::new(cx).await;
11018
11019    let diff_base = r#"
11020        use some::mod;
11021
11022        const A: u32 = 42;
11023
11024        fn main() {
11025            println!("hello");
11026
11027            println!("world");
11028        }
11029        "#
11030    .unindent();
11031
11032    // Edits are modified, removed, modified, added
11033    cx.set_state(
11034        &r#"
11035        use some::modified;
11036
11037        ˇ
11038        fn main() {
11039            println!("hello there");
11040
11041            println!("around the");
11042            println!("world");
11043        }
11044        "#
11045        .unindent(),
11046    );
11047
11048    cx.set_head_text(&diff_base);
11049    executor.run_until_parked();
11050
11051    cx.update_editor(|editor, window, cx| {
11052        //Wrap around the bottom of the buffer
11053        for _ in 0..3 {
11054            editor.go_to_next_hunk(&GoToHunk, window, cx);
11055        }
11056    });
11057
11058    cx.assert_editor_state(
11059        &r#"
11060        ˇuse some::modified;
11061
11062
11063        fn main() {
11064            println!("hello there");
11065
11066            println!("around the");
11067            println!("world");
11068        }
11069        "#
11070        .unindent(),
11071    );
11072
11073    cx.update_editor(|editor, window, cx| {
11074        //Wrap around the top of the buffer
11075        for _ in 0..2 {
11076            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11077        }
11078    });
11079
11080    cx.assert_editor_state(
11081        &r#"
11082        use some::modified;
11083
11084
11085        fn main() {
11086        ˇ    println!("hello there");
11087
11088            println!("around the");
11089            println!("world");
11090        }
11091        "#
11092        .unindent(),
11093    );
11094
11095    cx.update_editor(|editor, window, cx| {
11096        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11097    });
11098
11099    cx.assert_editor_state(
11100        &r#"
11101        use some::modified;
11102
11103        ˇ
11104        fn main() {
11105            println!("hello there");
11106
11107            println!("around the");
11108            println!("world");
11109        }
11110        "#
11111        .unindent(),
11112    );
11113
11114    cx.update_editor(|editor, window, cx| {
11115        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11116    });
11117
11118    cx.assert_editor_state(
11119        &r#"
11120        ˇuse some::modified;
11121
11122
11123        fn main() {
11124            println!("hello there");
11125
11126            println!("around the");
11127            println!("world");
11128        }
11129        "#
11130        .unindent(),
11131    );
11132
11133    cx.update_editor(|editor, window, cx| {
11134        for _ in 0..2 {
11135            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11136        }
11137    });
11138
11139    cx.assert_editor_state(
11140        &r#"
11141        use some::modified;
11142
11143
11144        fn main() {
11145        ˇ    println!("hello there");
11146
11147            println!("around the");
11148            println!("world");
11149        }
11150        "#
11151        .unindent(),
11152    );
11153
11154    cx.update_editor(|editor, window, cx| {
11155        editor.fold(&Fold, window, cx);
11156    });
11157
11158    cx.update_editor(|editor, window, cx| {
11159        editor.go_to_next_hunk(&GoToHunk, window, cx);
11160    });
11161
11162    cx.assert_editor_state(
11163        &r#"
11164        ˇuse some::modified;
11165
11166
11167        fn main() {
11168            println!("hello there");
11169
11170            println!("around the");
11171            println!("world");
11172        }
11173        "#
11174        .unindent(),
11175    );
11176}
11177
11178#[test]
11179fn test_split_words() {
11180    fn split(text: &str) -> Vec<&str> {
11181        split_words(text).collect()
11182    }
11183
11184    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11185    assert_eq!(split("hello_world"), &["hello_", "world"]);
11186    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11187    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11188    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11189    assert_eq!(split("helloworld"), &["helloworld"]);
11190
11191    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11192}
11193
11194#[gpui::test]
11195async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11196    init_test(cx, |_| {});
11197
11198    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11199    let mut assert = |before, after| {
11200        let _state_context = cx.set_state(before);
11201        cx.run_until_parked();
11202        cx.update_editor(|editor, window, cx| {
11203            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11204        });
11205        cx.assert_editor_state(after);
11206    };
11207
11208    // Outside bracket jumps to outside of matching bracket
11209    assert("console.logˇ(var);", "console.log(var)ˇ;");
11210    assert("console.log(var)ˇ;", "console.logˇ(var);");
11211
11212    // Inside bracket jumps to inside of matching bracket
11213    assert("console.log(ˇvar);", "console.log(varˇ);");
11214    assert("console.log(varˇ);", "console.log(ˇvar);");
11215
11216    // When outside a bracket and inside, favor jumping to the inside bracket
11217    assert(
11218        "console.log('foo', [1, 2, 3]ˇ);",
11219        "console.log(ˇ'foo', [1, 2, 3]);",
11220    );
11221    assert(
11222        "console.log(ˇ'foo', [1, 2, 3]);",
11223        "console.log('foo', [1, 2, 3]ˇ);",
11224    );
11225
11226    // Bias forward if two options are equally likely
11227    assert(
11228        "let result = curried_fun()ˇ();",
11229        "let result = curried_fun()()ˇ;",
11230    );
11231
11232    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11233    assert(
11234        indoc! {"
11235            function test() {
11236                console.log('test')ˇ
11237            }"},
11238        indoc! {"
11239            function test() {
11240                console.logˇ('test')
11241            }"},
11242    );
11243}
11244
11245#[gpui::test]
11246async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11247    init_test(cx, |_| {});
11248
11249    let fs = FakeFs::new(cx.executor());
11250    fs.insert_tree(
11251        path!("/a"),
11252        json!({
11253            "main.rs": "fn main() { let a = 5; }",
11254            "other.rs": "// Test file",
11255        }),
11256    )
11257    .await;
11258    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11259
11260    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11261    language_registry.add(Arc::new(Language::new(
11262        LanguageConfig {
11263            name: "Rust".into(),
11264            matcher: LanguageMatcher {
11265                path_suffixes: vec!["rs".to_string()],
11266                ..Default::default()
11267            },
11268            brackets: BracketPairConfig {
11269                pairs: vec![BracketPair {
11270                    start: "{".to_string(),
11271                    end: "}".to_string(),
11272                    close: true,
11273                    surround: true,
11274                    newline: true,
11275                }],
11276                disabled_scopes_by_bracket_ix: Vec::new(),
11277            },
11278            ..Default::default()
11279        },
11280        Some(tree_sitter_rust::LANGUAGE.into()),
11281    )));
11282    let mut fake_servers = language_registry.register_fake_lsp(
11283        "Rust",
11284        FakeLspAdapter {
11285            capabilities: lsp::ServerCapabilities {
11286                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11287                    first_trigger_character: "{".to_string(),
11288                    more_trigger_character: None,
11289                }),
11290                ..Default::default()
11291            },
11292            ..Default::default()
11293        },
11294    );
11295
11296    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11297
11298    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11299
11300    let worktree_id = workspace
11301        .update(cx, |workspace, _, cx| {
11302            workspace.project().update(cx, |project, cx| {
11303                project.worktrees(cx).next().unwrap().read(cx).id()
11304            })
11305        })
11306        .unwrap();
11307
11308    let buffer = project
11309        .update(cx, |project, cx| {
11310            project.open_local_buffer(path!("/a/main.rs"), cx)
11311        })
11312        .await
11313        .unwrap();
11314    let editor_handle = workspace
11315        .update(cx, |workspace, window, cx| {
11316            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11317        })
11318        .unwrap()
11319        .await
11320        .unwrap()
11321        .downcast::<Editor>()
11322        .unwrap();
11323
11324    cx.executor().start_waiting();
11325    let fake_server = fake_servers.next().await.unwrap();
11326
11327    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11328        assert_eq!(
11329            params.text_document_position.text_document.uri,
11330            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11331        );
11332        assert_eq!(
11333            params.text_document_position.position,
11334            lsp::Position::new(0, 21),
11335        );
11336
11337        Ok(Some(vec![lsp::TextEdit {
11338            new_text: "]".to_string(),
11339            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11340        }]))
11341    });
11342
11343    editor_handle.update_in(cx, |editor, window, cx| {
11344        window.focus(&editor.focus_handle(cx));
11345        editor.change_selections(None, window, cx, |s| {
11346            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11347        });
11348        editor.handle_input("{", window, cx);
11349    });
11350
11351    cx.executor().run_until_parked();
11352
11353    buffer.update(cx, |buffer, _| {
11354        assert_eq!(
11355            buffer.text(),
11356            "fn main() { let a = {5}; }",
11357            "No extra braces from on type formatting should appear in the buffer"
11358        )
11359    });
11360}
11361
11362#[gpui::test]
11363async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11364    init_test(cx, |_| {});
11365
11366    let fs = FakeFs::new(cx.executor());
11367    fs.insert_tree(
11368        path!("/a"),
11369        json!({
11370            "main.rs": "fn main() { let a = 5; }",
11371            "other.rs": "// Test file",
11372        }),
11373    )
11374    .await;
11375
11376    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11377
11378    let server_restarts = Arc::new(AtomicUsize::new(0));
11379    let closure_restarts = Arc::clone(&server_restarts);
11380    let language_server_name = "test language server";
11381    let language_name: LanguageName = "Rust".into();
11382
11383    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11384    language_registry.add(Arc::new(Language::new(
11385        LanguageConfig {
11386            name: language_name.clone(),
11387            matcher: LanguageMatcher {
11388                path_suffixes: vec!["rs".to_string()],
11389                ..Default::default()
11390            },
11391            ..Default::default()
11392        },
11393        Some(tree_sitter_rust::LANGUAGE.into()),
11394    )));
11395    let mut fake_servers = language_registry.register_fake_lsp(
11396        "Rust",
11397        FakeLspAdapter {
11398            name: language_server_name,
11399            initialization_options: Some(json!({
11400                "testOptionValue": true
11401            })),
11402            initializer: Some(Box::new(move |fake_server| {
11403                let task_restarts = Arc::clone(&closure_restarts);
11404                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11405                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11406                    futures::future::ready(Ok(()))
11407                });
11408            })),
11409            ..Default::default()
11410        },
11411    );
11412
11413    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11414    let _buffer = project
11415        .update(cx, |project, cx| {
11416            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11417        })
11418        .await
11419        .unwrap();
11420    let _fake_server = fake_servers.next().await.unwrap();
11421    update_test_language_settings(cx, |language_settings| {
11422        language_settings.languages.insert(
11423            language_name.clone(),
11424            LanguageSettingsContent {
11425                tab_size: NonZeroU32::new(8),
11426                ..Default::default()
11427            },
11428        );
11429    });
11430    cx.executor().run_until_parked();
11431    assert_eq!(
11432        server_restarts.load(atomic::Ordering::Acquire),
11433        0,
11434        "Should not restart LSP server on an unrelated change"
11435    );
11436
11437    update_test_project_settings(cx, |project_settings| {
11438        project_settings.lsp.insert(
11439            "Some other server name".into(),
11440            LspSettings {
11441                binary: None,
11442                settings: None,
11443                initialization_options: Some(json!({
11444                    "some other init value": false
11445                })),
11446            },
11447        );
11448    });
11449    cx.executor().run_until_parked();
11450    assert_eq!(
11451        server_restarts.load(atomic::Ordering::Acquire),
11452        0,
11453        "Should not restart LSP server on an unrelated LSP settings change"
11454    );
11455
11456    update_test_project_settings(cx, |project_settings| {
11457        project_settings.lsp.insert(
11458            language_server_name.into(),
11459            LspSettings {
11460                binary: None,
11461                settings: None,
11462                initialization_options: Some(json!({
11463                    "anotherInitValue": false
11464                })),
11465            },
11466        );
11467    });
11468    cx.executor().run_until_parked();
11469    assert_eq!(
11470        server_restarts.load(atomic::Ordering::Acquire),
11471        1,
11472        "Should restart LSP server on a related LSP settings change"
11473    );
11474
11475    update_test_project_settings(cx, |project_settings| {
11476        project_settings.lsp.insert(
11477            language_server_name.into(),
11478            LspSettings {
11479                binary: None,
11480                settings: None,
11481                initialization_options: Some(json!({
11482                    "anotherInitValue": false
11483                })),
11484            },
11485        );
11486    });
11487    cx.executor().run_until_parked();
11488    assert_eq!(
11489        server_restarts.load(atomic::Ordering::Acquire),
11490        1,
11491        "Should not restart LSP server on a related LSP settings change that is the same"
11492    );
11493
11494    update_test_project_settings(cx, |project_settings| {
11495        project_settings.lsp.insert(
11496            language_server_name.into(),
11497            LspSettings {
11498                binary: None,
11499                settings: None,
11500                initialization_options: None,
11501            },
11502        );
11503    });
11504    cx.executor().run_until_parked();
11505    assert_eq!(
11506        server_restarts.load(atomic::Ordering::Acquire),
11507        2,
11508        "Should restart LSP server on another related LSP settings change"
11509    );
11510}
11511
11512#[gpui::test]
11513async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11514    init_test(cx, |_| {});
11515
11516    let mut cx = EditorLspTestContext::new_rust(
11517        lsp::ServerCapabilities {
11518            completion_provider: Some(lsp::CompletionOptions {
11519                trigger_characters: Some(vec![".".to_string()]),
11520                resolve_provider: Some(true),
11521                ..Default::default()
11522            }),
11523            ..Default::default()
11524        },
11525        cx,
11526    )
11527    .await;
11528
11529    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11530    cx.simulate_keystroke(".");
11531    let completion_item = lsp::CompletionItem {
11532        label: "some".into(),
11533        kind: Some(lsp::CompletionItemKind::SNIPPET),
11534        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11535        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11536            kind: lsp::MarkupKind::Markdown,
11537            value: "```rust\nSome(2)\n```".to_string(),
11538        })),
11539        deprecated: Some(false),
11540        sort_text: Some("fffffff2".to_string()),
11541        filter_text: Some("some".to_string()),
11542        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11543        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11544            range: lsp::Range {
11545                start: lsp::Position {
11546                    line: 0,
11547                    character: 22,
11548                },
11549                end: lsp::Position {
11550                    line: 0,
11551                    character: 22,
11552                },
11553            },
11554            new_text: "Some(2)".to_string(),
11555        })),
11556        additional_text_edits: Some(vec![lsp::TextEdit {
11557            range: lsp::Range {
11558                start: lsp::Position {
11559                    line: 0,
11560                    character: 20,
11561                },
11562                end: lsp::Position {
11563                    line: 0,
11564                    character: 22,
11565                },
11566            },
11567            new_text: "".to_string(),
11568        }]),
11569        ..Default::default()
11570    };
11571
11572    let closure_completion_item = completion_item.clone();
11573    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11574        let task_completion_item = closure_completion_item.clone();
11575        async move {
11576            Ok(Some(lsp::CompletionResponse::Array(vec![
11577                task_completion_item,
11578            ])))
11579        }
11580    });
11581
11582    request.next().await;
11583
11584    cx.condition(|editor, _| editor.context_menu_visible())
11585        .await;
11586    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11587        editor
11588            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11589            .unwrap()
11590    });
11591    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11592
11593    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11594        let task_completion_item = completion_item.clone();
11595        async move { Ok(task_completion_item) }
11596    })
11597    .next()
11598    .await
11599    .unwrap();
11600    apply_additional_edits.await.unwrap();
11601    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11602}
11603
11604#[gpui::test]
11605async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11606    init_test(cx, |_| {});
11607
11608    let mut cx = EditorLspTestContext::new_rust(
11609        lsp::ServerCapabilities {
11610            completion_provider: Some(lsp::CompletionOptions {
11611                trigger_characters: Some(vec![".".to_string()]),
11612                resolve_provider: Some(true),
11613                ..Default::default()
11614            }),
11615            ..Default::default()
11616        },
11617        cx,
11618    )
11619    .await;
11620
11621    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11622    cx.simulate_keystroke(".");
11623
11624    let item1 = lsp::CompletionItem {
11625        label: "method id()".to_string(),
11626        filter_text: Some("id".to_string()),
11627        detail: None,
11628        documentation: None,
11629        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11630            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11631            new_text: ".id".to_string(),
11632        })),
11633        ..lsp::CompletionItem::default()
11634    };
11635
11636    let item2 = lsp::CompletionItem {
11637        label: "other".to_string(),
11638        filter_text: Some("other".to_string()),
11639        detail: None,
11640        documentation: None,
11641        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11642            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11643            new_text: ".other".to_string(),
11644        })),
11645        ..lsp::CompletionItem::default()
11646    };
11647
11648    let item1 = item1.clone();
11649    cx.handle_request::<lsp::request::Completion, _, _>({
11650        let item1 = item1.clone();
11651        move |_, _, _| {
11652            let item1 = item1.clone();
11653            let item2 = item2.clone();
11654            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11655        }
11656    })
11657    .next()
11658    .await;
11659
11660    cx.condition(|editor, _| editor.context_menu_visible())
11661        .await;
11662    cx.update_editor(|editor, _, _| {
11663        let context_menu = editor.context_menu.borrow_mut();
11664        let context_menu = context_menu
11665            .as_ref()
11666            .expect("Should have the context menu deployed");
11667        match context_menu {
11668            CodeContextMenu::Completions(completions_menu) => {
11669                let completions = completions_menu.completions.borrow_mut();
11670                assert_eq!(
11671                    completions
11672                        .iter()
11673                        .map(|completion| &completion.label.text)
11674                        .collect::<Vec<_>>(),
11675                    vec!["method id()", "other"]
11676                )
11677            }
11678            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11679        }
11680    });
11681
11682    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11683        let item1 = item1.clone();
11684        move |_, item_to_resolve, _| {
11685            let item1 = item1.clone();
11686            async move {
11687                if item1 == item_to_resolve {
11688                    Ok(lsp::CompletionItem {
11689                        label: "method id()".to_string(),
11690                        filter_text: Some("id".to_string()),
11691                        detail: Some("Now resolved!".to_string()),
11692                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11693                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11694                            range: lsp::Range::new(
11695                                lsp::Position::new(0, 22),
11696                                lsp::Position::new(0, 22),
11697                            ),
11698                            new_text: ".id".to_string(),
11699                        })),
11700                        ..lsp::CompletionItem::default()
11701                    })
11702                } else {
11703                    Ok(item_to_resolve)
11704                }
11705            }
11706        }
11707    })
11708    .next()
11709    .await
11710    .unwrap();
11711    cx.run_until_parked();
11712
11713    cx.update_editor(|editor, window, cx| {
11714        editor.context_menu_next(&Default::default(), window, cx);
11715    });
11716
11717    cx.update_editor(|editor, _, _| {
11718        let context_menu = editor.context_menu.borrow_mut();
11719        let context_menu = context_menu
11720            .as_ref()
11721            .expect("Should have the context menu deployed");
11722        match context_menu {
11723            CodeContextMenu::Completions(completions_menu) => {
11724                let completions = completions_menu.completions.borrow_mut();
11725                assert_eq!(
11726                    completions
11727                        .iter()
11728                        .map(|completion| &completion.label.text)
11729                        .collect::<Vec<_>>(),
11730                    vec!["method id() Now resolved!", "other"],
11731                    "Should update first completion label, but not second as the filter text did not match."
11732                );
11733            }
11734            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11735        }
11736    });
11737}
11738
11739#[gpui::test]
11740async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
11741    init_test(cx, |_| {});
11742
11743    let mut cx = EditorLspTestContext::new_rust(
11744        lsp::ServerCapabilities {
11745            completion_provider: Some(lsp::CompletionOptions {
11746                trigger_characters: Some(vec![".".to_string()]),
11747                resolve_provider: Some(true),
11748                ..Default::default()
11749            }),
11750            ..Default::default()
11751        },
11752        cx,
11753    )
11754    .await;
11755
11756    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11757    cx.simulate_keystroke(".");
11758
11759    let unresolved_item_1 = lsp::CompletionItem {
11760        label: "id".to_string(),
11761        filter_text: Some("id".to_string()),
11762        detail: None,
11763        documentation: None,
11764        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11765            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11766            new_text: ".id".to_string(),
11767        })),
11768        ..lsp::CompletionItem::default()
11769    };
11770    let resolved_item_1 = lsp::CompletionItem {
11771        additional_text_edits: Some(vec![lsp::TextEdit {
11772            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11773            new_text: "!!".to_string(),
11774        }]),
11775        ..unresolved_item_1.clone()
11776    };
11777    let unresolved_item_2 = lsp::CompletionItem {
11778        label: "other".to_string(),
11779        filter_text: Some("other".to_string()),
11780        detail: None,
11781        documentation: None,
11782        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11783            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11784            new_text: ".other".to_string(),
11785        })),
11786        ..lsp::CompletionItem::default()
11787    };
11788    let resolved_item_2 = lsp::CompletionItem {
11789        additional_text_edits: Some(vec![lsp::TextEdit {
11790            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11791            new_text: "??".to_string(),
11792        }]),
11793        ..unresolved_item_2.clone()
11794    };
11795
11796    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11797    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11798    cx.lsp
11799        .server
11800        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11801            let unresolved_item_1 = unresolved_item_1.clone();
11802            let resolved_item_1 = resolved_item_1.clone();
11803            let unresolved_item_2 = unresolved_item_2.clone();
11804            let resolved_item_2 = resolved_item_2.clone();
11805            let resolve_requests_1 = resolve_requests_1.clone();
11806            let resolve_requests_2 = resolve_requests_2.clone();
11807            move |unresolved_request, _| {
11808                let unresolved_item_1 = unresolved_item_1.clone();
11809                let resolved_item_1 = resolved_item_1.clone();
11810                let unresolved_item_2 = unresolved_item_2.clone();
11811                let resolved_item_2 = resolved_item_2.clone();
11812                let resolve_requests_1 = resolve_requests_1.clone();
11813                let resolve_requests_2 = resolve_requests_2.clone();
11814                async move {
11815                    if unresolved_request == unresolved_item_1 {
11816                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11817                        Ok(resolved_item_1.clone())
11818                    } else if unresolved_request == unresolved_item_2 {
11819                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11820                        Ok(resolved_item_2.clone())
11821                    } else {
11822                        panic!("Unexpected completion item {unresolved_request:?}")
11823                    }
11824                }
11825            }
11826        })
11827        .detach();
11828
11829    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11830        let unresolved_item_1 = unresolved_item_1.clone();
11831        let unresolved_item_2 = unresolved_item_2.clone();
11832        async move {
11833            Ok(Some(lsp::CompletionResponse::Array(vec![
11834                unresolved_item_1,
11835                unresolved_item_2,
11836            ])))
11837        }
11838    })
11839    .next()
11840    .await;
11841
11842    cx.condition(|editor, _| editor.context_menu_visible())
11843        .await;
11844    cx.update_editor(|editor, _, _| {
11845        let context_menu = editor.context_menu.borrow_mut();
11846        let context_menu = context_menu
11847            .as_ref()
11848            .expect("Should have the context menu deployed");
11849        match context_menu {
11850            CodeContextMenu::Completions(completions_menu) => {
11851                let completions = completions_menu.completions.borrow_mut();
11852                assert_eq!(
11853                    completions
11854                        .iter()
11855                        .map(|completion| &completion.label.text)
11856                        .collect::<Vec<_>>(),
11857                    vec!["id", "other"]
11858                )
11859            }
11860            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11861        }
11862    });
11863    cx.run_until_parked();
11864
11865    cx.update_editor(|editor, window, cx| {
11866        editor.context_menu_next(&ContextMenuNext, window, cx);
11867    });
11868    cx.run_until_parked();
11869    cx.update_editor(|editor, window, cx| {
11870        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11871    });
11872    cx.run_until_parked();
11873    cx.update_editor(|editor, window, cx| {
11874        editor.context_menu_next(&ContextMenuNext, window, cx);
11875    });
11876    cx.run_until_parked();
11877    cx.update_editor(|editor, window, cx| {
11878        editor
11879            .compose_completion(&ComposeCompletion::default(), window, cx)
11880            .expect("No task returned")
11881    })
11882    .await
11883    .expect("Completion failed");
11884    cx.run_until_parked();
11885
11886    cx.update_editor(|editor, _, cx| {
11887        assert_eq!(
11888            resolve_requests_1.load(atomic::Ordering::Acquire),
11889            1,
11890            "Should always resolve once despite multiple selections"
11891        );
11892        assert_eq!(
11893            resolve_requests_2.load(atomic::Ordering::Acquire),
11894            1,
11895            "Should always resolve once after multiple selections and applying the completion"
11896        );
11897        assert_eq!(
11898            editor.text(cx),
11899            "fn main() { let a = ??.other; }",
11900            "Should use resolved data when applying the completion"
11901        );
11902    });
11903}
11904
11905#[gpui::test]
11906async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
11907    init_test(cx, |_| {});
11908
11909    let item_0 = lsp::CompletionItem {
11910        label: "abs".into(),
11911        insert_text: Some("abs".into()),
11912        data: Some(json!({ "very": "special"})),
11913        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11914        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11915            lsp::InsertReplaceEdit {
11916                new_text: "abs".to_string(),
11917                insert: lsp::Range::default(),
11918                replace: lsp::Range::default(),
11919            },
11920        )),
11921        ..lsp::CompletionItem::default()
11922    };
11923    let items = iter::once(item_0.clone())
11924        .chain((11..51).map(|i| lsp::CompletionItem {
11925            label: format!("item_{}", i),
11926            insert_text: Some(format!("item_{}", i)),
11927            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11928            ..lsp::CompletionItem::default()
11929        }))
11930        .collect::<Vec<_>>();
11931
11932    let default_commit_characters = vec!["?".to_string()];
11933    let default_data = json!({ "default": "data"});
11934    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11935    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11936    let default_edit_range = lsp::Range {
11937        start: lsp::Position {
11938            line: 0,
11939            character: 5,
11940        },
11941        end: lsp::Position {
11942            line: 0,
11943            character: 5,
11944        },
11945    };
11946
11947    let item_0_out = lsp::CompletionItem {
11948        commit_characters: Some(default_commit_characters.clone()),
11949        insert_text_format: Some(default_insert_text_format),
11950        ..item_0
11951    };
11952    let items_out = iter::once(item_0_out)
11953        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11954            commit_characters: Some(default_commit_characters.clone()),
11955            data: Some(default_data.clone()),
11956            insert_text_mode: Some(default_insert_text_mode),
11957            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11958                range: default_edit_range,
11959                new_text: item.label.clone(),
11960            })),
11961            ..item.clone()
11962        }))
11963        .collect::<Vec<lsp::CompletionItem>>();
11964
11965    let mut cx = EditorLspTestContext::new_rust(
11966        lsp::ServerCapabilities {
11967            completion_provider: Some(lsp::CompletionOptions {
11968                trigger_characters: Some(vec![".".to_string()]),
11969                resolve_provider: Some(true),
11970                ..Default::default()
11971            }),
11972            ..Default::default()
11973        },
11974        cx,
11975    )
11976    .await;
11977
11978    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11979    cx.simulate_keystroke(".");
11980
11981    let completion_data = default_data.clone();
11982    let completion_characters = default_commit_characters.clone();
11983    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11984        let default_data = completion_data.clone();
11985        let default_commit_characters = completion_characters.clone();
11986        let items = items.clone();
11987        async move {
11988            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11989                items,
11990                item_defaults: Some(lsp::CompletionListItemDefaults {
11991                    data: Some(default_data.clone()),
11992                    commit_characters: Some(default_commit_characters.clone()),
11993                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11994                        default_edit_range,
11995                    )),
11996                    insert_text_format: Some(default_insert_text_format),
11997                    insert_text_mode: Some(default_insert_text_mode),
11998                }),
11999                ..lsp::CompletionList::default()
12000            })))
12001        }
12002    })
12003    .next()
12004    .await;
12005
12006    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12007    cx.lsp
12008        .server
12009        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12010            let closure_resolved_items = resolved_items.clone();
12011            move |item_to_resolve, _| {
12012                let closure_resolved_items = closure_resolved_items.clone();
12013                async move {
12014                    closure_resolved_items.lock().push(item_to_resolve.clone());
12015                    Ok(item_to_resolve)
12016                }
12017            }
12018        })
12019        .detach();
12020
12021    cx.condition(|editor, _| editor.context_menu_visible())
12022        .await;
12023    cx.run_until_parked();
12024    cx.update_editor(|editor, _, _| {
12025        let menu = editor.context_menu.borrow_mut();
12026        match menu.as_ref().expect("should have the completions menu") {
12027            CodeContextMenu::Completions(completions_menu) => {
12028                assert_eq!(
12029                    completions_menu
12030                        .entries
12031                        .borrow()
12032                        .iter()
12033                        .map(|mat| mat.string.clone())
12034                        .collect::<Vec<String>>(),
12035                    items_out
12036                        .iter()
12037                        .map(|completion| completion.label.clone())
12038                        .collect::<Vec<String>>()
12039                );
12040            }
12041            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12042        }
12043    });
12044    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12045    // with 4 from the end.
12046    assert_eq!(
12047        *resolved_items.lock(),
12048        [
12049            &items_out[0..16],
12050            &items_out[items_out.len() - 4..items_out.len()]
12051        ]
12052        .concat()
12053        .iter()
12054        .cloned()
12055        .collect::<Vec<lsp::CompletionItem>>()
12056    );
12057    resolved_items.lock().clear();
12058
12059    cx.update_editor(|editor, window, cx| {
12060        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12061    });
12062    cx.run_until_parked();
12063    // Completions that have already been resolved are skipped.
12064    assert_eq!(
12065        *resolved_items.lock(),
12066        items_out[items_out.len() - 16..items_out.len() - 4]
12067            .iter()
12068            .cloned()
12069            .collect::<Vec<lsp::CompletionItem>>()
12070    );
12071    resolved_items.lock().clear();
12072}
12073
12074#[gpui::test]
12075async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12076    init_test(cx, |_| {});
12077
12078    let mut cx = EditorLspTestContext::new(
12079        Language::new(
12080            LanguageConfig {
12081                matcher: LanguageMatcher {
12082                    path_suffixes: vec!["jsx".into()],
12083                    ..Default::default()
12084                },
12085                overrides: [(
12086                    "element".into(),
12087                    LanguageConfigOverride {
12088                        word_characters: Override::Set(['-'].into_iter().collect()),
12089                        ..Default::default()
12090                    },
12091                )]
12092                .into_iter()
12093                .collect(),
12094                ..Default::default()
12095            },
12096            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12097        )
12098        .with_override_query("(jsx_self_closing_element) @element")
12099        .unwrap(),
12100        lsp::ServerCapabilities {
12101            completion_provider: Some(lsp::CompletionOptions {
12102                trigger_characters: Some(vec![":".to_string()]),
12103                ..Default::default()
12104            }),
12105            ..Default::default()
12106        },
12107        cx,
12108    )
12109    .await;
12110
12111    cx.lsp
12112        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12113            Ok(Some(lsp::CompletionResponse::Array(vec![
12114                lsp::CompletionItem {
12115                    label: "bg-blue".into(),
12116                    ..Default::default()
12117                },
12118                lsp::CompletionItem {
12119                    label: "bg-red".into(),
12120                    ..Default::default()
12121                },
12122                lsp::CompletionItem {
12123                    label: "bg-yellow".into(),
12124                    ..Default::default()
12125                },
12126            ])))
12127        });
12128
12129    cx.set_state(r#"<p class="bgˇ" />"#);
12130
12131    // Trigger completion when typing a dash, because the dash is an extra
12132    // word character in the 'element' scope, which contains the cursor.
12133    cx.simulate_keystroke("-");
12134    cx.executor().run_until_parked();
12135    cx.update_editor(|editor, _, _| {
12136        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12137        {
12138            assert_eq!(
12139                completion_menu_entries(&menu),
12140                &["bg-red", "bg-blue", "bg-yellow"]
12141            );
12142        } else {
12143            panic!("expected completion menu to be open");
12144        }
12145    });
12146
12147    cx.simulate_keystroke("l");
12148    cx.executor().run_until_parked();
12149    cx.update_editor(|editor, _, _| {
12150        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12151        {
12152            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12153        } else {
12154            panic!("expected completion menu to be open");
12155        }
12156    });
12157
12158    // When filtering completions, consider the character after the '-' to
12159    // be the start of a subword.
12160    cx.set_state(r#"<p class="yelˇ" />"#);
12161    cx.simulate_keystroke("l");
12162    cx.executor().run_until_parked();
12163    cx.update_editor(|editor, _, _| {
12164        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12165        {
12166            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12167        } else {
12168            panic!("expected completion menu to be open");
12169        }
12170    });
12171}
12172
12173fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12174    let entries = menu.entries.borrow();
12175    entries.iter().map(|mat| mat.string.clone()).collect()
12176}
12177
12178#[gpui::test]
12179async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12180    init_test(cx, |settings| {
12181        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12182            FormatterList(vec![Formatter::Prettier].into()),
12183        ))
12184    });
12185
12186    let fs = FakeFs::new(cx.executor());
12187    fs.insert_file(path!("/file.ts"), Default::default()).await;
12188
12189    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12190    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12191
12192    language_registry.add(Arc::new(Language::new(
12193        LanguageConfig {
12194            name: "TypeScript".into(),
12195            matcher: LanguageMatcher {
12196                path_suffixes: vec!["ts".to_string()],
12197                ..Default::default()
12198            },
12199            ..Default::default()
12200        },
12201        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12202    )));
12203    update_test_language_settings(cx, |settings| {
12204        settings.defaults.prettier = Some(PrettierSettings {
12205            allowed: true,
12206            ..PrettierSettings::default()
12207        });
12208    });
12209
12210    let test_plugin = "test_plugin";
12211    let _ = language_registry.register_fake_lsp(
12212        "TypeScript",
12213        FakeLspAdapter {
12214            prettier_plugins: vec![test_plugin],
12215            ..Default::default()
12216        },
12217    );
12218
12219    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12220    let buffer = project
12221        .update(cx, |project, cx| {
12222            project.open_local_buffer(path!("/file.ts"), cx)
12223        })
12224        .await
12225        .unwrap();
12226
12227    let buffer_text = "one\ntwo\nthree\n";
12228    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12229    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12230    editor.update_in(cx, |editor, window, cx| {
12231        editor.set_text(buffer_text, window, cx)
12232    });
12233
12234    editor
12235        .update_in(cx, |editor, window, cx| {
12236            editor.perform_format(
12237                project.clone(),
12238                FormatTrigger::Manual,
12239                FormatTarget::Buffers,
12240                window,
12241                cx,
12242            )
12243        })
12244        .unwrap()
12245        .await;
12246    assert_eq!(
12247        editor.update(cx, |editor, cx| editor.text(cx)),
12248        buffer_text.to_string() + prettier_format_suffix,
12249        "Test prettier formatting was not applied to the original buffer text",
12250    );
12251
12252    update_test_language_settings(cx, |settings| {
12253        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12254    });
12255    let format = editor.update_in(cx, |editor, window, cx| {
12256        editor.perform_format(
12257            project.clone(),
12258            FormatTrigger::Manual,
12259            FormatTarget::Buffers,
12260            window,
12261            cx,
12262        )
12263    });
12264    format.await.unwrap();
12265    assert_eq!(
12266        editor.update(cx, |editor, cx| editor.text(cx)),
12267        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12268        "Autoformatting (via test prettier) was not applied to the original buffer text",
12269    );
12270}
12271
12272#[gpui::test]
12273async fn test_addition_reverts(cx: &mut TestAppContext) {
12274    init_test(cx, |_| {});
12275    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12276    let base_text = indoc! {r#"
12277        struct Row;
12278        struct Row1;
12279        struct Row2;
12280
12281        struct Row4;
12282        struct Row5;
12283        struct Row6;
12284
12285        struct Row8;
12286        struct Row9;
12287        struct Row10;"#};
12288
12289    // When addition hunks are not adjacent to carets, no hunk revert is performed
12290    assert_hunk_revert(
12291        indoc! {r#"struct Row;
12292                   struct Row1;
12293                   struct Row1.1;
12294                   struct Row1.2;
12295                   struct Row2;ˇ
12296
12297                   struct Row4;
12298                   struct Row5;
12299                   struct Row6;
12300
12301                   struct Row8;
12302                   ˇstruct Row9;
12303                   struct Row9.1;
12304                   struct Row9.2;
12305                   struct Row9.3;
12306                   struct Row10;"#},
12307        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12308        indoc! {r#"struct Row;
12309                   struct Row1;
12310                   struct Row1.1;
12311                   struct Row1.2;
12312                   struct Row2;ˇ
12313
12314                   struct Row4;
12315                   struct Row5;
12316                   struct Row6;
12317
12318                   struct Row8;
12319                   ˇstruct Row9;
12320                   struct Row9.1;
12321                   struct Row9.2;
12322                   struct Row9.3;
12323                   struct Row10;"#},
12324        base_text,
12325        &mut cx,
12326    );
12327    // Same for selections
12328    assert_hunk_revert(
12329        indoc! {r#"struct Row;
12330                   struct Row1;
12331                   struct Row2;
12332                   struct Row2.1;
12333                   struct Row2.2;
12334                   «ˇ
12335                   struct Row4;
12336                   struct» Row5;
12337                   «struct Row6;
12338                   ˇ»
12339                   struct Row9.1;
12340                   struct Row9.2;
12341                   struct Row9.3;
12342                   struct Row8;
12343                   struct Row9;
12344                   struct Row10;"#},
12345        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12346        indoc! {r#"struct Row;
12347                   struct Row1;
12348                   struct Row2;
12349                   struct Row2.1;
12350                   struct Row2.2;
12351                   «ˇ
12352                   struct Row4;
12353                   struct» Row5;
12354                   «struct Row6;
12355                   ˇ»
12356                   struct Row9.1;
12357                   struct Row9.2;
12358                   struct Row9.3;
12359                   struct Row8;
12360                   struct Row9;
12361                   struct Row10;"#},
12362        base_text,
12363        &mut cx,
12364    );
12365
12366    // When carets and selections intersect the addition hunks, those are reverted.
12367    // Adjacent carets got merged.
12368    assert_hunk_revert(
12369        indoc! {r#"struct Row;
12370                   ˇ// something on the top
12371                   struct Row1;
12372                   struct Row2;
12373                   struct Roˇw3.1;
12374                   struct Row2.2;
12375                   struct Row2.3;ˇ
12376
12377                   struct Row4;
12378                   struct ˇRow5.1;
12379                   struct Row5.2;
12380                   struct «Rowˇ»5.3;
12381                   struct Row5;
12382                   struct Row6;
12383                   ˇ
12384                   struct Row9.1;
12385                   struct «Rowˇ»9.2;
12386                   struct «ˇRow»9.3;
12387                   struct Row8;
12388                   struct Row9;
12389                   «ˇ// something on bottom»
12390                   struct Row10;"#},
12391        vec![
12392            DiffHunkStatus::added_none(),
12393            DiffHunkStatus::added_none(),
12394            DiffHunkStatus::added_none(),
12395            DiffHunkStatus::added_none(),
12396            DiffHunkStatus::added_none(),
12397        ],
12398        indoc! {r#"struct Row;
12399                   ˇstruct Row1;
12400                   struct Row2;
12401                   ˇ
12402                   struct Row4;
12403                   ˇstruct Row5;
12404                   struct Row6;
12405                   ˇ
12406                   ˇstruct Row8;
12407                   struct Row9;
12408                   ˇstruct Row10;"#},
12409        base_text,
12410        &mut cx,
12411    );
12412}
12413
12414#[gpui::test]
12415async fn test_modification_reverts(cx: &mut TestAppContext) {
12416    init_test(cx, |_| {});
12417    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12418    let base_text = indoc! {r#"
12419        struct Row;
12420        struct Row1;
12421        struct Row2;
12422
12423        struct Row4;
12424        struct Row5;
12425        struct Row6;
12426
12427        struct Row8;
12428        struct Row9;
12429        struct Row10;"#};
12430
12431    // Modification hunks behave the same as the addition ones.
12432    assert_hunk_revert(
12433        indoc! {r#"struct Row;
12434                   struct Row1;
12435                   struct Row33;
12436                   ˇ
12437                   struct Row4;
12438                   struct Row5;
12439                   struct Row6;
12440                   ˇ
12441                   struct Row99;
12442                   struct Row9;
12443                   struct Row10;"#},
12444        vec![
12445            DiffHunkStatus::modified_none(),
12446            DiffHunkStatus::modified_none(),
12447        ],
12448        indoc! {r#"struct Row;
12449                   struct Row1;
12450                   struct Row33;
12451                   ˇ
12452                   struct Row4;
12453                   struct Row5;
12454                   struct Row6;
12455                   ˇ
12456                   struct Row99;
12457                   struct Row9;
12458                   struct Row10;"#},
12459        base_text,
12460        &mut cx,
12461    );
12462    assert_hunk_revert(
12463        indoc! {r#"struct Row;
12464                   struct Row1;
12465                   struct Row33;
12466                   «ˇ
12467                   struct Row4;
12468                   struct» Row5;
12469                   «struct Row6;
12470                   ˇ»
12471                   struct Row99;
12472                   struct Row9;
12473                   struct Row10;"#},
12474        vec![
12475            DiffHunkStatus::modified_none(),
12476            DiffHunkStatus::modified_none(),
12477        ],
12478        indoc! {r#"struct Row;
12479                   struct Row1;
12480                   struct Row33;
12481                   «ˇ
12482                   struct Row4;
12483                   struct» Row5;
12484                   «struct Row6;
12485                   ˇ»
12486                   struct Row99;
12487                   struct Row9;
12488                   struct Row10;"#},
12489        base_text,
12490        &mut cx,
12491    );
12492
12493    assert_hunk_revert(
12494        indoc! {r#"ˇstruct Row1.1;
12495                   struct Row1;
12496                   «ˇstr»uct Row22;
12497
12498                   struct ˇRow44;
12499                   struct Row5;
12500                   struct «Rˇ»ow66;ˇ
12501
12502                   «struˇ»ct Row88;
12503                   struct Row9;
12504                   struct Row1011;ˇ"#},
12505        vec![
12506            DiffHunkStatus::modified_none(),
12507            DiffHunkStatus::modified_none(),
12508            DiffHunkStatus::modified_none(),
12509            DiffHunkStatus::modified_none(),
12510            DiffHunkStatus::modified_none(),
12511            DiffHunkStatus::modified_none(),
12512        ],
12513        indoc! {r#"struct Row;
12514                   ˇstruct Row1;
12515                   struct Row2;
12516                   ˇ
12517                   struct Row4;
12518                   ˇstruct Row5;
12519                   struct Row6;
12520                   ˇ
12521                   struct Row8;
12522                   ˇstruct Row9;
12523                   struct Row10;ˇ"#},
12524        base_text,
12525        &mut cx,
12526    );
12527}
12528
12529#[gpui::test]
12530async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12531    init_test(cx, |_| {});
12532    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12533    let base_text = indoc! {r#"
12534        one
12535
12536        two
12537        three
12538        "#};
12539
12540    cx.set_head_text(base_text);
12541    cx.set_state("\nˇ\n");
12542    cx.executor().run_until_parked();
12543    cx.update_editor(|editor, _window, cx| {
12544        editor.expand_selected_diff_hunks(cx);
12545    });
12546    cx.executor().run_until_parked();
12547    cx.update_editor(|editor, window, cx| {
12548        editor.backspace(&Default::default(), window, cx);
12549    });
12550    cx.run_until_parked();
12551    cx.assert_state_with_diff(
12552        indoc! {r#"
12553
12554        - two
12555        - threeˇ
12556        +
12557        "#}
12558        .to_string(),
12559    );
12560}
12561
12562#[gpui::test]
12563async fn test_deletion_reverts(cx: &mut TestAppContext) {
12564    init_test(cx, |_| {});
12565    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12566    let base_text = indoc! {r#"struct Row;
12567struct Row1;
12568struct Row2;
12569
12570struct Row4;
12571struct Row5;
12572struct Row6;
12573
12574struct Row8;
12575struct Row9;
12576struct Row10;"#};
12577
12578    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12579    assert_hunk_revert(
12580        indoc! {r#"struct Row;
12581                   struct Row2;
12582
12583                   ˇstruct Row4;
12584                   struct Row5;
12585                   struct Row6;
12586                   ˇ
12587                   struct Row8;
12588                   struct Row10;"#},
12589        vec![
12590            DiffHunkStatus::deleted_none(),
12591            DiffHunkStatus::deleted_none(),
12592        ],
12593        indoc! {r#"struct Row;
12594                   struct Row2;
12595
12596                   ˇstruct Row4;
12597                   struct Row5;
12598                   struct Row6;
12599                   ˇ
12600                   struct Row8;
12601                   struct Row10;"#},
12602        base_text,
12603        &mut cx,
12604    );
12605    assert_hunk_revert(
12606        indoc! {r#"struct Row;
12607                   struct Row2;
12608
12609                   «ˇstruct Row4;
12610                   struct» Row5;
12611                   «struct Row6;
12612                   ˇ»
12613                   struct Row8;
12614                   struct Row10;"#},
12615        vec![
12616            DiffHunkStatus::deleted_none(),
12617            DiffHunkStatus::deleted_none(),
12618        ],
12619        indoc! {r#"struct Row;
12620                   struct Row2;
12621
12622                   «ˇstruct Row4;
12623                   struct» Row5;
12624                   «struct Row6;
12625                   ˇ»
12626                   struct Row8;
12627                   struct Row10;"#},
12628        base_text,
12629        &mut cx,
12630    );
12631
12632    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12633    assert_hunk_revert(
12634        indoc! {r#"struct Row;
12635                   ˇstruct Row2;
12636
12637                   struct Row4;
12638                   struct Row5;
12639                   struct Row6;
12640
12641                   struct Row8;ˇ
12642                   struct Row10;"#},
12643        vec![
12644            DiffHunkStatus::deleted_none(),
12645            DiffHunkStatus::deleted_none(),
12646        ],
12647        indoc! {r#"struct Row;
12648                   struct Row1;
12649                   ˇstruct Row2;
12650
12651                   struct Row4;
12652                   struct Row5;
12653                   struct Row6;
12654
12655                   struct Row8;ˇ
12656                   struct Row9;
12657                   struct Row10;"#},
12658        base_text,
12659        &mut cx,
12660    );
12661    assert_hunk_revert(
12662        indoc! {r#"struct Row;
12663                   struct Row2«ˇ;
12664                   struct Row4;
12665                   struct» Row5;
12666                   «struct Row6;
12667
12668                   struct Row8;ˇ»
12669                   struct Row10;"#},
12670        vec![
12671            DiffHunkStatus::deleted_none(),
12672            DiffHunkStatus::deleted_none(),
12673            DiffHunkStatus::deleted_none(),
12674        ],
12675        indoc! {r#"struct Row;
12676                   struct Row1;
12677                   struct Row2«ˇ;
12678
12679                   struct Row4;
12680                   struct» Row5;
12681                   «struct Row6;
12682
12683                   struct Row8;ˇ»
12684                   struct Row9;
12685                   struct Row10;"#},
12686        base_text,
12687        &mut cx,
12688    );
12689}
12690
12691#[gpui::test]
12692async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
12693    init_test(cx, |_| {});
12694
12695    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12696    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12697    let base_text_3 =
12698        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12699
12700    let text_1 = edit_first_char_of_every_line(base_text_1);
12701    let text_2 = edit_first_char_of_every_line(base_text_2);
12702    let text_3 = edit_first_char_of_every_line(base_text_3);
12703
12704    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12705    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12706    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12707
12708    let multibuffer = cx.new(|cx| {
12709        let mut multibuffer = MultiBuffer::new(ReadWrite);
12710        multibuffer.push_excerpts(
12711            buffer_1.clone(),
12712            [
12713                ExcerptRange {
12714                    context: Point::new(0, 0)..Point::new(3, 0),
12715                    primary: None,
12716                },
12717                ExcerptRange {
12718                    context: Point::new(5, 0)..Point::new(7, 0),
12719                    primary: None,
12720                },
12721                ExcerptRange {
12722                    context: Point::new(9, 0)..Point::new(10, 4),
12723                    primary: None,
12724                },
12725            ],
12726            cx,
12727        );
12728        multibuffer.push_excerpts(
12729            buffer_2.clone(),
12730            [
12731                ExcerptRange {
12732                    context: Point::new(0, 0)..Point::new(3, 0),
12733                    primary: None,
12734                },
12735                ExcerptRange {
12736                    context: Point::new(5, 0)..Point::new(7, 0),
12737                    primary: None,
12738                },
12739                ExcerptRange {
12740                    context: Point::new(9, 0)..Point::new(10, 4),
12741                    primary: None,
12742                },
12743            ],
12744            cx,
12745        );
12746        multibuffer.push_excerpts(
12747            buffer_3.clone(),
12748            [
12749                ExcerptRange {
12750                    context: Point::new(0, 0)..Point::new(3, 0),
12751                    primary: None,
12752                },
12753                ExcerptRange {
12754                    context: Point::new(5, 0)..Point::new(7, 0),
12755                    primary: None,
12756                },
12757                ExcerptRange {
12758                    context: Point::new(9, 0)..Point::new(10, 4),
12759                    primary: None,
12760                },
12761            ],
12762            cx,
12763        );
12764        multibuffer
12765    });
12766
12767    let fs = FakeFs::new(cx.executor());
12768    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
12769    let (editor, cx) = cx
12770        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
12771    editor.update_in(cx, |editor, _window, cx| {
12772        for (buffer, diff_base) in [
12773            (buffer_1.clone(), base_text_1),
12774            (buffer_2.clone(), base_text_2),
12775            (buffer_3.clone(), base_text_3),
12776        ] {
12777            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12778            editor
12779                .buffer
12780                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12781        }
12782    });
12783    cx.executor().run_until_parked();
12784
12785    editor.update_in(cx, |editor, window, cx| {
12786        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}");
12787        editor.select_all(&SelectAll, window, cx);
12788        editor.git_restore(&Default::default(), window, cx);
12789    });
12790    cx.executor().run_until_parked();
12791
12792    // When all ranges are selected, all buffer hunks are reverted.
12793    editor.update(cx, |editor, cx| {
12794        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");
12795    });
12796    buffer_1.update(cx, |buffer, _| {
12797        assert_eq!(buffer.text(), base_text_1);
12798    });
12799    buffer_2.update(cx, |buffer, _| {
12800        assert_eq!(buffer.text(), base_text_2);
12801    });
12802    buffer_3.update(cx, |buffer, _| {
12803        assert_eq!(buffer.text(), base_text_3);
12804    });
12805
12806    editor.update_in(cx, |editor, window, cx| {
12807        editor.undo(&Default::default(), window, cx);
12808    });
12809
12810    editor.update_in(cx, |editor, window, cx| {
12811        editor.change_selections(None, window, cx, |s| {
12812            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12813        });
12814        editor.git_restore(&Default::default(), window, cx);
12815    });
12816
12817    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12818    // but not affect buffer_2 and its related excerpts.
12819    editor.update(cx, |editor, cx| {
12820        assert_eq!(
12821            editor.text(cx),
12822            "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}"
12823        );
12824    });
12825    buffer_1.update(cx, |buffer, _| {
12826        assert_eq!(buffer.text(), base_text_1);
12827    });
12828    buffer_2.update(cx, |buffer, _| {
12829        assert_eq!(
12830            buffer.text(),
12831            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12832        );
12833    });
12834    buffer_3.update(cx, |buffer, _| {
12835        assert_eq!(
12836            buffer.text(),
12837            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12838        );
12839    });
12840
12841    fn edit_first_char_of_every_line(text: &str) -> String {
12842        text.split('\n')
12843            .map(|line| format!("X{}", &line[1..]))
12844            .collect::<Vec<_>>()
12845            .join("\n")
12846    }
12847}
12848
12849#[gpui::test]
12850async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
12851    init_test(cx, |_| {});
12852
12853    let cols = 4;
12854    let rows = 10;
12855    let sample_text_1 = sample_text(rows, cols, 'a');
12856    assert_eq!(
12857        sample_text_1,
12858        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12859    );
12860    let sample_text_2 = sample_text(rows, cols, 'l');
12861    assert_eq!(
12862        sample_text_2,
12863        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12864    );
12865    let sample_text_3 = sample_text(rows, cols, 'v');
12866    assert_eq!(
12867        sample_text_3,
12868        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12869    );
12870
12871    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12872    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12873    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12874
12875    let multi_buffer = cx.new(|cx| {
12876        let mut multibuffer = MultiBuffer::new(ReadWrite);
12877        multibuffer.push_excerpts(
12878            buffer_1.clone(),
12879            [
12880                ExcerptRange {
12881                    context: Point::new(0, 0)..Point::new(3, 0),
12882                    primary: None,
12883                },
12884                ExcerptRange {
12885                    context: Point::new(5, 0)..Point::new(7, 0),
12886                    primary: None,
12887                },
12888                ExcerptRange {
12889                    context: Point::new(9, 0)..Point::new(10, 4),
12890                    primary: None,
12891                },
12892            ],
12893            cx,
12894        );
12895        multibuffer.push_excerpts(
12896            buffer_2.clone(),
12897            [
12898                ExcerptRange {
12899                    context: Point::new(0, 0)..Point::new(3, 0),
12900                    primary: None,
12901                },
12902                ExcerptRange {
12903                    context: Point::new(5, 0)..Point::new(7, 0),
12904                    primary: None,
12905                },
12906                ExcerptRange {
12907                    context: Point::new(9, 0)..Point::new(10, 4),
12908                    primary: None,
12909                },
12910            ],
12911            cx,
12912        );
12913        multibuffer.push_excerpts(
12914            buffer_3.clone(),
12915            [
12916                ExcerptRange {
12917                    context: Point::new(0, 0)..Point::new(3, 0),
12918                    primary: None,
12919                },
12920                ExcerptRange {
12921                    context: Point::new(5, 0)..Point::new(7, 0),
12922                    primary: None,
12923                },
12924                ExcerptRange {
12925                    context: Point::new(9, 0)..Point::new(10, 4),
12926                    primary: None,
12927                },
12928            ],
12929            cx,
12930        );
12931        multibuffer
12932    });
12933
12934    let fs = FakeFs::new(cx.executor());
12935    fs.insert_tree(
12936        "/a",
12937        json!({
12938            "main.rs": sample_text_1,
12939            "other.rs": sample_text_2,
12940            "lib.rs": sample_text_3,
12941        }),
12942    )
12943    .await;
12944    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12945    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12946    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12947    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12948        Editor::new(
12949            EditorMode::Full,
12950            multi_buffer,
12951            Some(project.clone()),
12952            true,
12953            window,
12954            cx,
12955        )
12956    });
12957    let multibuffer_item_id = workspace
12958        .update(cx, |workspace, window, cx| {
12959            assert!(
12960                workspace.active_item(cx).is_none(),
12961                "active item should be None before the first item is added"
12962            );
12963            workspace.add_item_to_active_pane(
12964                Box::new(multi_buffer_editor.clone()),
12965                None,
12966                true,
12967                window,
12968                cx,
12969            );
12970            let active_item = workspace
12971                .active_item(cx)
12972                .expect("should have an active item after adding the multi buffer");
12973            assert!(
12974                !active_item.is_singleton(cx),
12975                "A multi buffer was expected to active after adding"
12976            );
12977            active_item.item_id()
12978        })
12979        .unwrap();
12980    cx.executor().run_until_parked();
12981
12982    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12983        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12984            s.select_ranges(Some(1..2))
12985        });
12986        editor.open_excerpts(&OpenExcerpts, window, cx);
12987    });
12988    cx.executor().run_until_parked();
12989    let first_item_id = workspace
12990        .update(cx, |workspace, window, cx| {
12991            let active_item = workspace
12992                .active_item(cx)
12993                .expect("should have an active item after navigating into the 1st buffer");
12994            let first_item_id = active_item.item_id();
12995            assert_ne!(
12996                first_item_id, multibuffer_item_id,
12997                "Should navigate into the 1st buffer and activate it"
12998            );
12999            assert!(
13000                active_item.is_singleton(cx),
13001                "New active item should be a singleton buffer"
13002            );
13003            assert_eq!(
13004                active_item
13005                    .act_as::<Editor>(cx)
13006                    .expect("should have navigated into an editor for the 1st buffer")
13007                    .read(cx)
13008                    .text(cx),
13009                sample_text_1
13010            );
13011
13012            workspace
13013                .go_back(workspace.active_pane().downgrade(), window, cx)
13014                .detach_and_log_err(cx);
13015
13016            first_item_id
13017        })
13018        .unwrap();
13019    cx.executor().run_until_parked();
13020    workspace
13021        .update(cx, |workspace, _, cx| {
13022            let active_item = workspace
13023                .active_item(cx)
13024                .expect("should have an active item after navigating back");
13025            assert_eq!(
13026                active_item.item_id(),
13027                multibuffer_item_id,
13028                "Should navigate back to the multi buffer"
13029            );
13030            assert!(!active_item.is_singleton(cx));
13031        })
13032        .unwrap();
13033
13034    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13035        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13036            s.select_ranges(Some(39..40))
13037        });
13038        editor.open_excerpts(&OpenExcerpts, window, cx);
13039    });
13040    cx.executor().run_until_parked();
13041    let second_item_id = workspace
13042        .update(cx, |workspace, window, cx| {
13043            let active_item = workspace
13044                .active_item(cx)
13045                .expect("should have an active item after navigating into the 2nd buffer");
13046            let second_item_id = active_item.item_id();
13047            assert_ne!(
13048                second_item_id, multibuffer_item_id,
13049                "Should navigate away from the multibuffer"
13050            );
13051            assert_ne!(
13052                second_item_id, first_item_id,
13053                "Should navigate into the 2nd buffer and activate it"
13054            );
13055            assert!(
13056                active_item.is_singleton(cx),
13057                "New active item should be a singleton buffer"
13058            );
13059            assert_eq!(
13060                active_item
13061                    .act_as::<Editor>(cx)
13062                    .expect("should have navigated into an editor")
13063                    .read(cx)
13064                    .text(cx),
13065                sample_text_2
13066            );
13067
13068            workspace
13069                .go_back(workspace.active_pane().downgrade(), window, cx)
13070                .detach_and_log_err(cx);
13071
13072            second_item_id
13073        })
13074        .unwrap();
13075    cx.executor().run_until_parked();
13076    workspace
13077        .update(cx, |workspace, _, cx| {
13078            let active_item = workspace
13079                .active_item(cx)
13080                .expect("should have an active item after navigating back from the 2nd buffer");
13081            assert_eq!(
13082                active_item.item_id(),
13083                multibuffer_item_id,
13084                "Should navigate back from the 2nd buffer to the multi buffer"
13085            );
13086            assert!(!active_item.is_singleton(cx));
13087        })
13088        .unwrap();
13089
13090    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13091        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13092            s.select_ranges(Some(70..70))
13093        });
13094        editor.open_excerpts(&OpenExcerpts, window, cx);
13095    });
13096    cx.executor().run_until_parked();
13097    workspace
13098        .update(cx, |workspace, window, cx| {
13099            let active_item = workspace
13100                .active_item(cx)
13101                .expect("should have an active item after navigating into the 3rd buffer");
13102            let third_item_id = active_item.item_id();
13103            assert_ne!(
13104                third_item_id, multibuffer_item_id,
13105                "Should navigate into the 3rd buffer and activate it"
13106            );
13107            assert_ne!(third_item_id, first_item_id);
13108            assert_ne!(third_item_id, second_item_id);
13109            assert!(
13110                active_item.is_singleton(cx),
13111                "New active item should be a singleton buffer"
13112            );
13113            assert_eq!(
13114                active_item
13115                    .act_as::<Editor>(cx)
13116                    .expect("should have navigated into an editor")
13117                    .read(cx)
13118                    .text(cx),
13119                sample_text_3
13120            );
13121
13122            workspace
13123                .go_back(workspace.active_pane().downgrade(), window, cx)
13124                .detach_and_log_err(cx);
13125        })
13126        .unwrap();
13127    cx.executor().run_until_parked();
13128    workspace
13129        .update(cx, |workspace, _, cx| {
13130            let active_item = workspace
13131                .active_item(cx)
13132                .expect("should have an active item after navigating back from the 3rd buffer");
13133            assert_eq!(
13134                active_item.item_id(),
13135                multibuffer_item_id,
13136                "Should navigate back from the 3rd buffer to the multi buffer"
13137            );
13138            assert!(!active_item.is_singleton(cx));
13139        })
13140        .unwrap();
13141}
13142
13143#[gpui::test]
13144async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13145    init_test(cx, |_| {});
13146
13147    let mut cx = EditorTestContext::new(cx).await;
13148
13149    let diff_base = r#"
13150        use some::mod;
13151
13152        const A: u32 = 42;
13153
13154        fn main() {
13155            println!("hello");
13156
13157            println!("world");
13158        }
13159        "#
13160    .unindent();
13161
13162    cx.set_state(
13163        &r#"
13164        use some::modified;
13165
13166        ˇ
13167        fn main() {
13168            println!("hello there");
13169
13170            println!("around the");
13171            println!("world");
13172        }
13173        "#
13174        .unindent(),
13175    );
13176
13177    cx.set_head_text(&diff_base);
13178    executor.run_until_parked();
13179
13180    cx.update_editor(|editor, window, cx| {
13181        editor.go_to_next_hunk(&GoToHunk, window, cx);
13182        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13183    });
13184    executor.run_until_parked();
13185    cx.assert_state_with_diff(
13186        r#"
13187          use some::modified;
13188
13189
13190          fn main() {
13191        -     println!("hello");
13192        + ˇ    println!("hello there");
13193
13194              println!("around the");
13195              println!("world");
13196          }
13197        "#
13198        .unindent(),
13199    );
13200
13201    cx.update_editor(|editor, window, cx| {
13202        for _ in 0..2 {
13203            editor.go_to_next_hunk(&GoToHunk, window, cx);
13204            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13205        }
13206    });
13207    executor.run_until_parked();
13208    cx.assert_state_with_diff(
13209        r#"
13210        - use some::mod;
13211        + ˇuse some::modified;
13212
13213
13214          fn main() {
13215        -     println!("hello");
13216        +     println!("hello there");
13217
13218        +     println!("around the");
13219              println!("world");
13220          }
13221        "#
13222        .unindent(),
13223    );
13224
13225    cx.update_editor(|editor, window, cx| {
13226        editor.go_to_next_hunk(&GoToHunk, window, cx);
13227        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13228    });
13229    executor.run_until_parked();
13230    cx.assert_state_with_diff(
13231        r#"
13232        - use some::mod;
13233        + use some::modified;
13234
13235        - const A: u32 = 42;
13236          ˇ
13237          fn main() {
13238        -     println!("hello");
13239        +     println!("hello there");
13240
13241        +     println!("around the");
13242              println!("world");
13243          }
13244        "#
13245        .unindent(),
13246    );
13247
13248    cx.update_editor(|editor, window, cx| {
13249        editor.cancel(&Cancel, window, cx);
13250    });
13251
13252    cx.assert_state_with_diff(
13253        r#"
13254          use some::modified;
13255
13256          ˇ
13257          fn main() {
13258              println!("hello there");
13259
13260              println!("around the");
13261              println!("world");
13262          }
13263        "#
13264        .unindent(),
13265    );
13266}
13267
13268#[gpui::test]
13269async fn test_diff_base_change_with_expanded_diff_hunks(
13270    executor: BackgroundExecutor,
13271    cx: &mut TestAppContext,
13272) {
13273    init_test(cx, |_| {});
13274
13275    let mut cx = EditorTestContext::new(cx).await;
13276
13277    let diff_base = r#"
13278        use some::mod1;
13279        use some::mod2;
13280
13281        const A: u32 = 42;
13282        const B: u32 = 42;
13283        const C: u32 = 42;
13284
13285        fn main() {
13286            println!("hello");
13287
13288            println!("world");
13289        }
13290        "#
13291    .unindent();
13292
13293    cx.set_state(
13294        &r#"
13295        use some::mod2;
13296
13297        const A: u32 = 42;
13298        const C: u32 = 42;
13299
13300        fn main(ˇ) {
13301            //println!("hello");
13302
13303            println!("world");
13304            //
13305            //
13306        }
13307        "#
13308        .unindent(),
13309    );
13310
13311    cx.set_head_text(&diff_base);
13312    executor.run_until_parked();
13313
13314    cx.update_editor(|editor, window, cx| {
13315        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13316    });
13317    executor.run_until_parked();
13318    cx.assert_state_with_diff(
13319        r#"
13320        - use some::mod1;
13321          use some::mod2;
13322
13323          const A: u32 = 42;
13324        - const B: u32 = 42;
13325          const C: u32 = 42;
13326
13327          fn main(ˇ) {
13328        -     println!("hello");
13329        +     //println!("hello");
13330
13331              println!("world");
13332        +     //
13333        +     //
13334          }
13335        "#
13336        .unindent(),
13337    );
13338
13339    cx.set_head_text("new diff base!");
13340    executor.run_until_parked();
13341    cx.assert_state_with_diff(
13342        r#"
13343        - new diff base!
13344        + use some::mod2;
13345        +
13346        + const A: u32 = 42;
13347        + const C: u32 = 42;
13348        +
13349        + fn main(ˇ) {
13350        +     //println!("hello");
13351        +
13352        +     println!("world");
13353        +     //
13354        +     //
13355        + }
13356        "#
13357        .unindent(),
13358    );
13359}
13360
13361#[gpui::test]
13362async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13363    init_test(cx, |_| {});
13364
13365    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13366    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13367    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13368    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13369    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13370    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13371
13372    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13373    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13374    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13375
13376    let multi_buffer = cx.new(|cx| {
13377        let mut multibuffer = MultiBuffer::new(ReadWrite);
13378        multibuffer.push_excerpts(
13379            buffer_1.clone(),
13380            [
13381                ExcerptRange {
13382                    context: Point::new(0, 0)..Point::new(3, 0),
13383                    primary: None,
13384                },
13385                ExcerptRange {
13386                    context: Point::new(5, 0)..Point::new(7, 0),
13387                    primary: None,
13388                },
13389                ExcerptRange {
13390                    context: Point::new(9, 0)..Point::new(10, 3),
13391                    primary: None,
13392                },
13393            ],
13394            cx,
13395        );
13396        multibuffer.push_excerpts(
13397            buffer_2.clone(),
13398            [
13399                ExcerptRange {
13400                    context: Point::new(0, 0)..Point::new(3, 0),
13401                    primary: None,
13402                },
13403                ExcerptRange {
13404                    context: Point::new(5, 0)..Point::new(7, 0),
13405                    primary: None,
13406                },
13407                ExcerptRange {
13408                    context: Point::new(9, 0)..Point::new(10, 3),
13409                    primary: None,
13410                },
13411            ],
13412            cx,
13413        );
13414        multibuffer.push_excerpts(
13415            buffer_3.clone(),
13416            [
13417                ExcerptRange {
13418                    context: Point::new(0, 0)..Point::new(3, 0),
13419                    primary: None,
13420                },
13421                ExcerptRange {
13422                    context: Point::new(5, 0)..Point::new(7, 0),
13423                    primary: None,
13424                },
13425                ExcerptRange {
13426                    context: Point::new(9, 0)..Point::new(10, 3),
13427                    primary: None,
13428                },
13429            ],
13430            cx,
13431        );
13432        multibuffer
13433    });
13434
13435    let editor = cx.add_window(|window, cx| {
13436        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13437    });
13438    editor
13439        .update(cx, |editor, _window, cx| {
13440            for (buffer, diff_base) in [
13441                (buffer_1.clone(), file_1_old),
13442                (buffer_2.clone(), file_2_old),
13443                (buffer_3.clone(), file_3_old),
13444            ] {
13445                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13446                editor
13447                    .buffer
13448                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13449            }
13450        })
13451        .unwrap();
13452
13453    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13454    cx.run_until_parked();
13455
13456    cx.assert_editor_state(
13457        &"
13458            ˇaaa
13459            ccc
13460            ddd
13461
13462            ggg
13463            hhh
13464
13465
13466            lll
13467            mmm
13468            NNN
13469
13470            qqq
13471            rrr
13472
13473            uuu
13474            111
13475            222
13476            333
13477
13478            666
13479            777
13480
13481            000
13482            !!!"
13483        .unindent(),
13484    );
13485
13486    cx.update_editor(|editor, window, cx| {
13487        editor.select_all(&SelectAll, window, cx);
13488        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13489    });
13490    cx.executor().run_until_parked();
13491
13492    cx.assert_state_with_diff(
13493        "
13494            «aaa
13495          - bbb
13496            ccc
13497            ddd
13498
13499            ggg
13500            hhh
13501
13502
13503            lll
13504            mmm
13505          - nnn
13506          + NNN
13507
13508            qqq
13509            rrr
13510
13511            uuu
13512            111
13513            222
13514            333
13515
13516          + 666
13517            777
13518
13519            000
13520            !!!ˇ»"
13521            .unindent(),
13522    );
13523}
13524
13525#[gpui::test]
13526async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13527    init_test(cx, |_| {});
13528
13529    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13530    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13531
13532    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13533    let multi_buffer = cx.new(|cx| {
13534        let mut multibuffer = MultiBuffer::new(ReadWrite);
13535        multibuffer.push_excerpts(
13536            buffer.clone(),
13537            [
13538                ExcerptRange {
13539                    context: Point::new(0, 0)..Point::new(2, 0),
13540                    primary: None,
13541                },
13542                ExcerptRange {
13543                    context: Point::new(4, 0)..Point::new(7, 0),
13544                    primary: None,
13545                },
13546                ExcerptRange {
13547                    context: Point::new(9, 0)..Point::new(10, 0),
13548                    primary: None,
13549                },
13550            ],
13551            cx,
13552        );
13553        multibuffer
13554    });
13555
13556    let editor = cx.add_window(|window, cx| {
13557        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13558    });
13559    editor
13560        .update(cx, |editor, _window, cx| {
13561            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13562            editor
13563                .buffer
13564                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13565        })
13566        .unwrap();
13567
13568    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13569    cx.run_until_parked();
13570
13571    cx.update_editor(|editor, window, cx| {
13572        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13573    });
13574    cx.executor().run_until_parked();
13575
13576    // When the start of a hunk coincides with the start of its excerpt,
13577    // the hunk is expanded. When the start of a a hunk is earlier than
13578    // the start of its excerpt, the hunk is not expanded.
13579    cx.assert_state_with_diff(
13580        "
13581            ˇaaa
13582          - bbb
13583          + BBB
13584
13585          - ddd
13586          - eee
13587          + DDD
13588          + EEE
13589            fff
13590
13591            iii
13592        "
13593        .unindent(),
13594    );
13595}
13596
13597#[gpui::test]
13598async fn test_edits_around_expanded_insertion_hunks(
13599    executor: BackgroundExecutor,
13600    cx: &mut TestAppContext,
13601) {
13602    init_test(cx, |_| {});
13603
13604    let mut cx = EditorTestContext::new(cx).await;
13605
13606    let diff_base = r#"
13607        use some::mod1;
13608        use some::mod2;
13609
13610        const A: u32 = 42;
13611
13612        fn main() {
13613            println!("hello");
13614
13615            println!("world");
13616        }
13617        "#
13618    .unindent();
13619    executor.run_until_parked();
13620    cx.set_state(
13621        &r#"
13622        use some::mod1;
13623        use some::mod2;
13624
13625        const A: u32 = 42;
13626        const B: u32 = 42;
13627        const C: u32 = 42;
13628        ˇ
13629
13630        fn main() {
13631            println!("hello");
13632
13633            println!("world");
13634        }
13635        "#
13636        .unindent(),
13637    );
13638
13639    cx.set_head_text(&diff_base);
13640    executor.run_until_parked();
13641
13642    cx.update_editor(|editor, window, cx| {
13643        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13644    });
13645    executor.run_until_parked();
13646
13647    cx.assert_state_with_diff(
13648        r#"
13649        use some::mod1;
13650        use some::mod2;
13651
13652        const A: u32 = 42;
13653      + const B: u32 = 42;
13654      + const C: u32 = 42;
13655      + ˇ
13656
13657        fn main() {
13658            println!("hello");
13659
13660            println!("world");
13661        }
13662      "#
13663        .unindent(),
13664    );
13665
13666    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13667    executor.run_until_parked();
13668
13669    cx.assert_state_with_diff(
13670        r#"
13671        use some::mod1;
13672        use some::mod2;
13673
13674        const A: u32 = 42;
13675      + const B: u32 = 42;
13676      + const C: u32 = 42;
13677      + const D: u32 = 42;
13678      + ˇ
13679
13680        fn main() {
13681            println!("hello");
13682
13683            println!("world");
13684        }
13685      "#
13686        .unindent(),
13687    );
13688
13689    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13690    executor.run_until_parked();
13691
13692    cx.assert_state_with_diff(
13693        r#"
13694        use some::mod1;
13695        use some::mod2;
13696
13697        const A: u32 = 42;
13698      + const B: u32 = 42;
13699      + const C: u32 = 42;
13700      + const D: u32 = 42;
13701      + const E: u32 = 42;
13702      + ˇ
13703
13704        fn main() {
13705            println!("hello");
13706
13707            println!("world");
13708        }
13709      "#
13710        .unindent(),
13711    );
13712
13713    cx.update_editor(|editor, window, cx| {
13714        editor.delete_line(&DeleteLine, window, cx);
13715    });
13716    executor.run_until_parked();
13717
13718    cx.assert_state_with_diff(
13719        r#"
13720        use some::mod1;
13721        use some::mod2;
13722
13723        const A: u32 = 42;
13724      + const B: u32 = 42;
13725      + const C: u32 = 42;
13726      + const D: u32 = 42;
13727      + const E: u32 = 42;
13728        ˇ
13729        fn main() {
13730            println!("hello");
13731
13732            println!("world");
13733        }
13734      "#
13735        .unindent(),
13736    );
13737
13738    cx.update_editor(|editor, window, cx| {
13739        editor.move_up(&MoveUp, window, cx);
13740        editor.delete_line(&DeleteLine, window, cx);
13741        editor.move_up(&MoveUp, window, cx);
13742        editor.delete_line(&DeleteLine, window, cx);
13743        editor.move_up(&MoveUp, window, cx);
13744        editor.delete_line(&DeleteLine, window, cx);
13745    });
13746    executor.run_until_parked();
13747    cx.assert_state_with_diff(
13748        r#"
13749        use some::mod1;
13750        use some::mod2;
13751
13752        const A: u32 = 42;
13753      + const B: u32 = 42;
13754        ˇ
13755        fn main() {
13756            println!("hello");
13757
13758            println!("world");
13759        }
13760      "#
13761        .unindent(),
13762    );
13763
13764    cx.update_editor(|editor, window, cx| {
13765        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13766        editor.delete_line(&DeleteLine, window, cx);
13767    });
13768    executor.run_until_parked();
13769    cx.assert_state_with_diff(
13770        r#"
13771        ˇ
13772        fn main() {
13773            println!("hello");
13774
13775            println!("world");
13776        }
13777      "#
13778        .unindent(),
13779    );
13780}
13781
13782#[gpui::test]
13783async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13784    init_test(cx, |_| {});
13785
13786    let mut cx = EditorTestContext::new(cx).await;
13787    cx.set_head_text(indoc! { "
13788        one
13789        two
13790        three
13791        four
13792        five
13793        "
13794    });
13795    cx.set_state(indoc! { "
13796        one
13797        ˇthree
13798        five
13799    "});
13800    cx.run_until_parked();
13801    cx.update_editor(|editor, window, cx| {
13802        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13803    });
13804    cx.assert_state_with_diff(
13805        indoc! { "
13806        one
13807      - two
13808        ˇthree
13809      - four
13810        five
13811    "}
13812        .to_string(),
13813    );
13814    cx.update_editor(|editor, window, cx| {
13815        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13816    });
13817
13818    cx.assert_state_with_diff(
13819        indoc! { "
13820        one
13821        ˇthree
13822        five
13823    "}
13824        .to_string(),
13825    );
13826
13827    cx.set_state(indoc! { "
13828        one
13829        ˇTWO
13830        three
13831        four
13832        five
13833    "});
13834    cx.run_until_parked();
13835    cx.update_editor(|editor, window, cx| {
13836        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13837    });
13838
13839    cx.assert_state_with_diff(
13840        indoc! { "
13841            one
13842          - two
13843          + ˇTWO
13844            three
13845            four
13846            five
13847        "}
13848        .to_string(),
13849    );
13850    cx.update_editor(|editor, window, cx| {
13851        editor.move_up(&Default::default(), window, cx);
13852        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13853    });
13854    cx.assert_state_with_diff(
13855        indoc! { "
13856            one
13857            ˇTWO
13858            three
13859            four
13860            five
13861        "}
13862        .to_string(),
13863    );
13864}
13865
13866#[gpui::test]
13867async fn test_edits_around_expanded_deletion_hunks(
13868    executor: BackgroundExecutor,
13869    cx: &mut TestAppContext,
13870) {
13871    init_test(cx, |_| {});
13872
13873    let mut cx = EditorTestContext::new(cx).await;
13874
13875    let diff_base = r#"
13876        use some::mod1;
13877        use some::mod2;
13878
13879        const A: u32 = 42;
13880        const B: u32 = 42;
13881        const C: u32 = 42;
13882
13883
13884        fn main() {
13885            println!("hello");
13886
13887            println!("world");
13888        }
13889    "#
13890    .unindent();
13891    executor.run_until_parked();
13892    cx.set_state(
13893        &r#"
13894        use some::mod1;
13895        use some::mod2;
13896
13897        ˇconst B: u32 = 42;
13898        const C: u32 = 42;
13899
13900
13901        fn main() {
13902            println!("hello");
13903
13904            println!("world");
13905        }
13906        "#
13907        .unindent(),
13908    );
13909
13910    cx.set_head_text(&diff_base);
13911    executor.run_until_parked();
13912
13913    cx.update_editor(|editor, window, cx| {
13914        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13915    });
13916    executor.run_until_parked();
13917
13918    cx.assert_state_with_diff(
13919        r#"
13920        use some::mod1;
13921        use some::mod2;
13922
13923      - const A: u32 = 42;
13924        ˇconst B: u32 = 42;
13925        const C: u32 = 42;
13926
13927
13928        fn main() {
13929            println!("hello");
13930
13931            println!("world");
13932        }
13933      "#
13934        .unindent(),
13935    );
13936
13937    cx.update_editor(|editor, window, cx| {
13938        editor.delete_line(&DeleteLine, window, cx);
13939    });
13940    executor.run_until_parked();
13941    cx.assert_state_with_diff(
13942        r#"
13943        use some::mod1;
13944        use some::mod2;
13945
13946      - const A: u32 = 42;
13947      - const B: u32 = 42;
13948        ˇconst C: u32 = 42;
13949
13950
13951        fn main() {
13952            println!("hello");
13953
13954            println!("world");
13955        }
13956      "#
13957        .unindent(),
13958    );
13959
13960    cx.update_editor(|editor, window, cx| {
13961        editor.delete_line(&DeleteLine, window, cx);
13962    });
13963    executor.run_until_parked();
13964    cx.assert_state_with_diff(
13965        r#"
13966        use some::mod1;
13967        use some::mod2;
13968
13969      - const A: u32 = 42;
13970      - const B: u32 = 42;
13971      - const C: u32 = 42;
13972        ˇ
13973
13974        fn main() {
13975            println!("hello");
13976
13977            println!("world");
13978        }
13979      "#
13980        .unindent(),
13981    );
13982
13983    cx.update_editor(|editor, window, cx| {
13984        editor.handle_input("replacement", window, cx);
13985    });
13986    executor.run_until_parked();
13987    cx.assert_state_with_diff(
13988        r#"
13989        use some::mod1;
13990        use some::mod2;
13991
13992      - const A: u32 = 42;
13993      - const B: u32 = 42;
13994      - const C: u32 = 42;
13995      -
13996      + replacementˇ
13997
13998        fn main() {
13999            println!("hello");
14000
14001            println!("world");
14002        }
14003      "#
14004        .unindent(),
14005    );
14006}
14007
14008#[gpui::test]
14009async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14010    init_test(cx, |_| {});
14011
14012    let mut cx = EditorTestContext::new(cx).await;
14013
14014    let base_text = r#"
14015        one
14016        two
14017        three
14018        four
14019        five
14020    "#
14021    .unindent();
14022    executor.run_until_parked();
14023    cx.set_state(
14024        &r#"
14025        one
14026        two
14027        fˇour
14028        five
14029        "#
14030        .unindent(),
14031    );
14032
14033    cx.set_head_text(&base_text);
14034    executor.run_until_parked();
14035
14036    cx.update_editor(|editor, window, cx| {
14037        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14038    });
14039    executor.run_until_parked();
14040
14041    cx.assert_state_with_diff(
14042        r#"
14043          one
14044          two
14045        - three
14046          fˇour
14047          five
14048        "#
14049        .unindent(),
14050    );
14051
14052    cx.update_editor(|editor, window, cx| {
14053        editor.backspace(&Backspace, window, cx);
14054        editor.backspace(&Backspace, window, cx);
14055    });
14056    executor.run_until_parked();
14057    cx.assert_state_with_diff(
14058        r#"
14059          one
14060          two
14061        - threeˇ
14062        - four
14063        + our
14064          five
14065        "#
14066        .unindent(),
14067    );
14068}
14069
14070#[gpui::test]
14071async fn test_edit_after_expanded_modification_hunk(
14072    executor: BackgroundExecutor,
14073    cx: &mut TestAppContext,
14074) {
14075    init_test(cx, |_| {});
14076
14077    let mut cx = EditorTestContext::new(cx).await;
14078
14079    let diff_base = r#"
14080        use some::mod1;
14081        use some::mod2;
14082
14083        const A: u32 = 42;
14084        const B: u32 = 42;
14085        const C: u32 = 42;
14086        const D: u32 = 42;
14087
14088
14089        fn main() {
14090            println!("hello");
14091
14092            println!("world");
14093        }"#
14094    .unindent();
14095
14096    cx.set_state(
14097        &r#"
14098        use some::mod1;
14099        use some::mod2;
14100
14101        const A: u32 = 42;
14102        const B: u32 = 42;
14103        const C: u32 = 43ˇ
14104        const D: u32 = 42;
14105
14106
14107        fn main() {
14108            println!("hello");
14109
14110            println!("world");
14111        }"#
14112        .unindent(),
14113    );
14114
14115    cx.set_head_text(&diff_base);
14116    executor.run_until_parked();
14117    cx.update_editor(|editor, window, cx| {
14118        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14119    });
14120    executor.run_until_parked();
14121
14122    cx.assert_state_with_diff(
14123        r#"
14124        use some::mod1;
14125        use some::mod2;
14126
14127        const A: u32 = 42;
14128        const B: u32 = 42;
14129      - const C: u32 = 42;
14130      + const C: u32 = 43ˇ
14131        const D: u32 = 42;
14132
14133
14134        fn main() {
14135            println!("hello");
14136
14137            println!("world");
14138        }"#
14139        .unindent(),
14140    );
14141
14142    cx.update_editor(|editor, window, cx| {
14143        editor.handle_input("\nnew_line\n", window, cx);
14144    });
14145    executor.run_until_parked();
14146
14147    cx.assert_state_with_diff(
14148        r#"
14149        use some::mod1;
14150        use some::mod2;
14151
14152        const A: u32 = 42;
14153        const B: u32 = 42;
14154      - const C: u32 = 42;
14155      + const C: u32 = 43
14156      + new_line
14157      + ˇ
14158        const D: u32 = 42;
14159
14160
14161        fn main() {
14162            println!("hello");
14163
14164            println!("world");
14165        }"#
14166        .unindent(),
14167    );
14168}
14169
14170#[gpui::test]
14171async fn test_stage_and_unstage_added_file_hunk(
14172    executor: BackgroundExecutor,
14173    cx: &mut TestAppContext,
14174) {
14175    init_test(cx, |_| {});
14176
14177    let mut cx = EditorTestContext::new(cx).await;
14178    cx.update_editor(|editor, _, cx| {
14179        editor.set_expand_all_diff_hunks(cx);
14180    });
14181
14182    let working_copy = r#"
14183            ˇfn main() {
14184                println!("hello, world!");
14185            }
14186        "#
14187    .unindent();
14188
14189    cx.set_state(&working_copy);
14190    executor.run_until_parked();
14191
14192    cx.assert_state_with_diff(
14193        r#"
14194            + ˇfn main() {
14195            +     println!("hello, world!");
14196            + }
14197        "#
14198        .unindent(),
14199    );
14200    cx.assert_index_text(None);
14201
14202    cx.update_editor(|editor, window, cx| {
14203        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14204    });
14205    executor.run_until_parked();
14206    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14207    cx.assert_state_with_diff(
14208        r#"
14209            + ˇfn main() {
14210            +     println!("hello, world!");
14211            + }
14212        "#
14213        .unindent(),
14214    );
14215
14216    cx.update_editor(|editor, window, cx| {
14217        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14218    });
14219    executor.run_until_parked();
14220    cx.assert_index_text(None);
14221}
14222
14223async fn setup_indent_guides_editor(
14224    text: &str,
14225    cx: &mut TestAppContext,
14226) -> (BufferId, EditorTestContext) {
14227    init_test(cx, |_| {});
14228
14229    let mut cx = EditorTestContext::new(cx).await;
14230
14231    let buffer_id = cx.update_editor(|editor, window, cx| {
14232        editor.set_text(text, window, cx);
14233        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14234
14235        buffer_ids[0]
14236    });
14237
14238    (buffer_id, cx)
14239}
14240
14241fn assert_indent_guides(
14242    range: Range<u32>,
14243    expected: Vec<IndentGuide>,
14244    active_indices: Option<Vec<usize>>,
14245    cx: &mut EditorTestContext,
14246) {
14247    let indent_guides = cx.update_editor(|editor, window, cx| {
14248        let snapshot = editor.snapshot(window, cx).display_snapshot;
14249        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14250            editor,
14251            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14252            true,
14253            &snapshot,
14254            cx,
14255        );
14256
14257        indent_guides.sort_by(|a, b| {
14258            a.depth.cmp(&b.depth).then(
14259                a.start_row
14260                    .cmp(&b.start_row)
14261                    .then(a.end_row.cmp(&b.end_row)),
14262            )
14263        });
14264        indent_guides
14265    });
14266
14267    if let Some(expected) = active_indices {
14268        let active_indices = cx.update_editor(|editor, window, cx| {
14269            let snapshot = editor.snapshot(window, cx).display_snapshot;
14270            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14271        });
14272
14273        assert_eq!(
14274            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14275            expected,
14276            "Active indent guide indices do not match"
14277        );
14278    }
14279
14280    assert_eq!(indent_guides, expected, "Indent guides do not match");
14281}
14282
14283fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14284    IndentGuide {
14285        buffer_id,
14286        start_row: MultiBufferRow(start_row),
14287        end_row: MultiBufferRow(end_row),
14288        depth,
14289        tab_size: 4,
14290        settings: IndentGuideSettings {
14291            enabled: true,
14292            line_width: 1,
14293            active_line_width: 1,
14294            ..Default::default()
14295        },
14296    }
14297}
14298
14299#[gpui::test]
14300async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14301    let (buffer_id, mut cx) = setup_indent_guides_editor(
14302        &"
14303    fn main() {
14304        let a = 1;
14305    }"
14306        .unindent(),
14307        cx,
14308    )
14309    .await;
14310
14311    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14312}
14313
14314#[gpui::test]
14315async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14316    let (buffer_id, mut cx) = setup_indent_guides_editor(
14317        &"
14318    fn main() {
14319        let a = 1;
14320        let b = 2;
14321    }"
14322        .unindent(),
14323        cx,
14324    )
14325    .await;
14326
14327    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14328}
14329
14330#[gpui::test]
14331async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14332    let (buffer_id, mut cx) = setup_indent_guides_editor(
14333        &"
14334    fn main() {
14335        let a = 1;
14336        if a == 3 {
14337            let b = 2;
14338        } else {
14339            let c = 3;
14340        }
14341    }"
14342        .unindent(),
14343        cx,
14344    )
14345    .await;
14346
14347    assert_indent_guides(
14348        0..8,
14349        vec![
14350            indent_guide(buffer_id, 1, 6, 0),
14351            indent_guide(buffer_id, 3, 3, 1),
14352            indent_guide(buffer_id, 5, 5, 1),
14353        ],
14354        None,
14355        &mut cx,
14356    );
14357}
14358
14359#[gpui::test]
14360async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14361    let (buffer_id, mut cx) = setup_indent_guides_editor(
14362        &"
14363    fn main() {
14364        let a = 1;
14365            let b = 2;
14366        let c = 3;
14367    }"
14368        .unindent(),
14369        cx,
14370    )
14371    .await;
14372
14373    assert_indent_guides(
14374        0..5,
14375        vec![
14376            indent_guide(buffer_id, 1, 3, 0),
14377            indent_guide(buffer_id, 2, 2, 1),
14378        ],
14379        None,
14380        &mut cx,
14381    );
14382}
14383
14384#[gpui::test]
14385async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14386    let (buffer_id, mut cx) = setup_indent_guides_editor(
14387        &"
14388        fn main() {
14389            let a = 1;
14390
14391            let c = 3;
14392        }"
14393        .unindent(),
14394        cx,
14395    )
14396    .await;
14397
14398    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14399}
14400
14401#[gpui::test]
14402async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14403    let (buffer_id, mut cx) = setup_indent_guides_editor(
14404        &"
14405        fn main() {
14406            let a = 1;
14407
14408            let c = 3;
14409
14410            if a == 3 {
14411                let b = 2;
14412            } else {
14413                let c = 3;
14414            }
14415        }"
14416        .unindent(),
14417        cx,
14418    )
14419    .await;
14420
14421    assert_indent_guides(
14422        0..11,
14423        vec![
14424            indent_guide(buffer_id, 1, 9, 0),
14425            indent_guide(buffer_id, 6, 6, 1),
14426            indent_guide(buffer_id, 8, 8, 1),
14427        ],
14428        None,
14429        &mut cx,
14430    );
14431}
14432
14433#[gpui::test]
14434async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14435    let (buffer_id, mut cx) = setup_indent_guides_editor(
14436        &"
14437        fn main() {
14438            let a = 1;
14439
14440            let c = 3;
14441
14442            if a == 3 {
14443                let b = 2;
14444            } else {
14445                let c = 3;
14446            }
14447        }"
14448        .unindent(),
14449        cx,
14450    )
14451    .await;
14452
14453    assert_indent_guides(
14454        1..11,
14455        vec![
14456            indent_guide(buffer_id, 1, 9, 0),
14457            indent_guide(buffer_id, 6, 6, 1),
14458            indent_guide(buffer_id, 8, 8, 1),
14459        ],
14460        None,
14461        &mut cx,
14462    );
14463}
14464
14465#[gpui::test]
14466async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14467    let (buffer_id, mut cx) = setup_indent_guides_editor(
14468        &"
14469        fn main() {
14470            let a = 1;
14471
14472            let c = 3;
14473
14474            if a == 3 {
14475                let b = 2;
14476            } else {
14477                let c = 3;
14478            }
14479        }"
14480        .unindent(),
14481        cx,
14482    )
14483    .await;
14484
14485    assert_indent_guides(
14486        1..10,
14487        vec![
14488            indent_guide(buffer_id, 1, 9, 0),
14489            indent_guide(buffer_id, 6, 6, 1),
14490            indent_guide(buffer_id, 8, 8, 1),
14491        ],
14492        None,
14493        &mut cx,
14494    );
14495}
14496
14497#[gpui::test]
14498async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14499    let (buffer_id, mut cx) = setup_indent_guides_editor(
14500        &"
14501        block1
14502            block2
14503                block3
14504                    block4
14505            block2
14506        block1
14507        block1"
14508            .unindent(),
14509        cx,
14510    )
14511    .await;
14512
14513    assert_indent_guides(
14514        1..10,
14515        vec![
14516            indent_guide(buffer_id, 1, 4, 0),
14517            indent_guide(buffer_id, 2, 3, 1),
14518            indent_guide(buffer_id, 3, 3, 2),
14519        ],
14520        None,
14521        &mut cx,
14522    );
14523}
14524
14525#[gpui::test]
14526async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14527    let (buffer_id, mut cx) = setup_indent_guides_editor(
14528        &"
14529        block1
14530            block2
14531                block3
14532
14533        block1
14534        block1"
14535            .unindent(),
14536        cx,
14537    )
14538    .await;
14539
14540    assert_indent_guides(
14541        0..6,
14542        vec![
14543            indent_guide(buffer_id, 1, 2, 0),
14544            indent_guide(buffer_id, 2, 2, 1),
14545        ],
14546        None,
14547        &mut cx,
14548    );
14549}
14550
14551#[gpui::test]
14552async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14553    let (buffer_id, mut cx) = setup_indent_guides_editor(
14554        &"
14555        block1
14556
14557
14558
14559            block2
14560        "
14561        .unindent(),
14562        cx,
14563    )
14564    .await;
14565
14566    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14567}
14568
14569#[gpui::test]
14570async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14571    let (buffer_id, mut cx) = setup_indent_guides_editor(
14572        &"
14573        def a:
14574        \tb = 3
14575        \tif True:
14576        \t\tc = 4
14577        \t\td = 5
14578        \tprint(b)
14579        "
14580        .unindent(),
14581        cx,
14582    )
14583    .await;
14584
14585    assert_indent_guides(
14586        0..6,
14587        vec![
14588            indent_guide(buffer_id, 1, 6, 0),
14589            indent_guide(buffer_id, 3, 4, 1),
14590        ],
14591        None,
14592        &mut cx,
14593    );
14594}
14595
14596#[gpui::test]
14597async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14598    let (buffer_id, mut cx) = setup_indent_guides_editor(
14599        &"
14600    fn main() {
14601        let a = 1;
14602    }"
14603        .unindent(),
14604        cx,
14605    )
14606    .await;
14607
14608    cx.update_editor(|editor, window, cx| {
14609        editor.change_selections(None, window, cx, |s| {
14610            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14611        });
14612    });
14613
14614    assert_indent_guides(
14615        0..3,
14616        vec![indent_guide(buffer_id, 1, 1, 0)],
14617        Some(vec![0]),
14618        &mut cx,
14619    );
14620}
14621
14622#[gpui::test]
14623async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14624    let (buffer_id, mut cx) = setup_indent_guides_editor(
14625        &"
14626    fn main() {
14627        if 1 == 2 {
14628            let a = 1;
14629        }
14630    }"
14631        .unindent(),
14632        cx,
14633    )
14634    .await;
14635
14636    cx.update_editor(|editor, window, cx| {
14637        editor.change_selections(None, window, cx, |s| {
14638            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14639        });
14640    });
14641
14642    assert_indent_guides(
14643        0..4,
14644        vec![
14645            indent_guide(buffer_id, 1, 3, 0),
14646            indent_guide(buffer_id, 2, 2, 1),
14647        ],
14648        Some(vec![1]),
14649        &mut cx,
14650    );
14651
14652    cx.update_editor(|editor, window, cx| {
14653        editor.change_selections(None, window, cx, |s| {
14654            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14655        });
14656    });
14657
14658    assert_indent_guides(
14659        0..4,
14660        vec![
14661            indent_guide(buffer_id, 1, 3, 0),
14662            indent_guide(buffer_id, 2, 2, 1),
14663        ],
14664        Some(vec![1]),
14665        &mut cx,
14666    );
14667
14668    cx.update_editor(|editor, window, cx| {
14669        editor.change_selections(None, window, cx, |s| {
14670            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14671        });
14672    });
14673
14674    assert_indent_guides(
14675        0..4,
14676        vec![
14677            indent_guide(buffer_id, 1, 3, 0),
14678            indent_guide(buffer_id, 2, 2, 1),
14679        ],
14680        Some(vec![0]),
14681        &mut cx,
14682    );
14683}
14684
14685#[gpui::test]
14686async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
14687    let (buffer_id, mut cx) = setup_indent_guides_editor(
14688        &"
14689    fn main() {
14690        let a = 1;
14691
14692        let b = 2;
14693    }"
14694        .unindent(),
14695        cx,
14696    )
14697    .await;
14698
14699    cx.update_editor(|editor, window, cx| {
14700        editor.change_selections(None, window, cx, |s| {
14701            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14702        });
14703    });
14704
14705    assert_indent_guides(
14706        0..5,
14707        vec![indent_guide(buffer_id, 1, 3, 0)],
14708        Some(vec![0]),
14709        &mut cx,
14710    );
14711}
14712
14713#[gpui::test]
14714async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
14715    let (buffer_id, mut cx) = setup_indent_guides_editor(
14716        &"
14717    def m:
14718        a = 1
14719        pass"
14720            .unindent(),
14721        cx,
14722    )
14723    .await;
14724
14725    cx.update_editor(|editor, window, cx| {
14726        editor.change_selections(None, window, cx, |s| {
14727            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14728        });
14729    });
14730
14731    assert_indent_guides(
14732        0..3,
14733        vec![indent_guide(buffer_id, 1, 2, 0)],
14734        Some(vec![0]),
14735        &mut cx,
14736    );
14737}
14738
14739#[gpui::test]
14740async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
14741    init_test(cx, |_| {});
14742    let mut cx = EditorTestContext::new(cx).await;
14743    let text = indoc! {
14744        "
14745        impl A {
14746            fn b() {
14747                0;
14748                3;
14749                5;
14750                6;
14751                7;
14752            }
14753        }
14754        "
14755    };
14756    let base_text = indoc! {
14757        "
14758        impl A {
14759            fn b() {
14760                0;
14761                1;
14762                2;
14763                3;
14764                4;
14765            }
14766            fn c() {
14767                5;
14768                6;
14769                7;
14770            }
14771        }
14772        "
14773    };
14774
14775    cx.update_editor(|editor, window, cx| {
14776        editor.set_text(text, window, cx);
14777
14778        editor.buffer().update(cx, |multibuffer, cx| {
14779            let buffer = multibuffer.as_singleton().unwrap();
14780            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14781
14782            multibuffer.set_all_diff_hunks_expanded(cx);
14783            multibuffer.add_diff(diff, cx);
14784
14785            buffer.read(cx).remote_id()
14786        })
14787    });
14788    cx.run_until_parked();
14789
14790    cx.assert_state_with_diff(
14791        indoc! { "
14792          impl A {
14793              fn b() {
14794                  0;
14795        -         1;
14796        -         2;
14797                  3;
14798        -         4;
14799        -     }
14800        -     fn c() {
14801                  5;
14802                  6;
14803                  7;
14804              }
14805          }
14806          ˇ"
14807        }
14808        .to_string(),
14809    );
14810
14811    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14812        editor
14813            .snapshot(window, cx)
14814            .buffer_snapshot
14815            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14816            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14817            .collect::<Vec<_>>()
14818    });
14819    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14820    assert_eq!(
14821        actual_guides,
14822        vec![
14823            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14824            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14825            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14826        ]
14827    );
14828}
14829
14830#[gpui::test]
14831async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14832    init_test(cx, |_| {});
14833    let mut cx = EditorTestContext::new(cx).await;
14834
14835    let diff_base = r#"
14836        a
14837        b
14838        c
14839        "#
14840    .unindent();
14841
14842    cx.set_state(
14843        &r#"
14844        ˇA
14845        b
14846        C
14847        "#
14848        .unindent(),
14849    );
14850    cx.set_head_text(&diff_base);
14851    cx.update_editor(|editor, window, cx| {
14852        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14853    });
14854    executor.run_until_parked();
14855
14856    let both_hunks_expanded = r#"
14857        - a
14858        + ˇA
14859          b
14860        - c
14861        + C
14862        "#
14863    .unindent();
14864
14865    cx.assert_state_with_diff(both_hunks_expanded.clone());
14866
14867    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14868        let snapshot = editor.snapshot(window, cx);
14869        let hunks = editor
14870            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14871            .collect::<Vec<_>>();
14872        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14873        let buffer_id = hunks[0].buffer_id;
14874        hunks
14875            .into_iter()
14876            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14877            .collect::<Vec<_>>()
14878    });
14879    assert_eq!(hunk_ranges.len(), 2);
14880
14881    cx.update_editor(|editor, _, cx| {
14882        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14883    });
14884    executor.run_until_parked();
14885
14886    let second_hunk_expanded = r#"
14887          ˇA
14888          b
14889        - c
14890        + C
14891        "#
14892    .unindent();
14893
14894    cx.assert_state_with_diff(second_hunk_expanded);
14895
14896    cx.update_editor(|editor, _, cx| {
14897        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14898    });
14899    executor.run_until_parked();
14900
14901    cx.assert_state_with_diff(both_hunks_expanded.clone());
14902
14903    cx.update_editor(|editor, _, cx| {
14904        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14905    });
14906    executor.run_until_parked();
14907
14908    let first_hunk_expanded = r#"
14909        - a
14910        + ˇA
14911          b
14912          C
14913        "#
14914    .unindent();
14915
14916    cx.assert_state_with_diff(first_hunk_expanded);
14917
14918    cx.update_editor(|editor, _, cx| {
14919        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14920    });
14921    executor.run_until_parked();
14922
14923    cx.assert_state_with_diff(both_hunks_expanded);
14924
14925    cx.set_state(
14926        &r#"
14927        ˇA
14928        b
14929        "#
14930        .unindent(),
14931    );
14932    cx.run_until_parked();
14933
14934    // TODO this cursor position seems bad
14935    cx.assert_state_with_diff(
14936        r#"
14937        - ˇa
14938        + A
14939          b
14940        "#
14941        .unindent(),
14942    );
14943
14944    cx.update_editor(|editor, window, cx| {
14945        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14946    });
14947
14948    cx.assert_state_with_diff(
14949        r#"
14950            - ˇa
14951            + A
14952              b
14953            - c
14954            "#
14955        .unindent(),
14956    );
14957
14958    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14959        let snapshot = editor.snapshot(window, cx);
14960        let hunks = editor
14961            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14962            .collect::<Vec<_>>();
14963        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14964        let buffer_id = hunks[0].buffer_id;
14965        hunks
14966            .into_iter()
14967            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14968            .collect::<Vec<_>>()
14969    });
14970    assert_eq!(hunk_ranges.len(), 2);
14971
14972    cx.update_editor(|editor, _, cx| {
14973        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14974    });
14975    executor.run_until_parked();
14976
14977    cx.assert_state_with_diff(
14978        r#"
14979        - ˇa
14980        + A
14981          b
14982        "#
14983        .unindent(),
14984    );
14985}
14986
14987#[gpui::test]
14988async fn test_display_diff_hunks(cx: &mut TestAppContext) {
14989    init_test(cx, |_| {});
14990
14991    let fs = FakeFs::new(cx.executor());
14992    fs.insert_tree(
14993        path!("/test"),
14994        json!({
14995            ".git": {},
14996            "file-1": "ONE\n",
14997            "file-2": "TWO\n",
14998            "file-3": "THREE\n",
14999        }),
15000    )
15001    .await;
15002
15003    fs.set_head_for_repo(
15004        path!("/test/.git").as_ref(),
15005        &[
15006            ("file-1".into(), "one\n".into()),
15007            ("file-2".into(), "two\n".into()),
15008            ("file-3".into(), "three\n".into()),
15009        ],
15010    );
15011
15012    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15013    let mut buffers = vec![];
15014    for i in 1..=3 {
15015        let buffer = project
15016            .update(cx, |project, cx| {
15017                let path = format!(path!("/test/file-{}"), i);
15018                project.open_local_buffer(path, cx)
15019            })
15020            .await
15021            .unwrap();
15022        buffers.push(buffer);
15023    }
15024
15025    let multibuffer = cx.new(|cx| {
15026        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15027        multibuffer.set_all_diff_hunks_expanded(cx);
15028        for buffer in &buffers {
15029            let snapshot = buffer.read(cx).snapshot();
15030            multibuffer.set_excerpts_for_path(
15031                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15032                buffer.clone(),
15033                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15034                DEFAULT_MULTIBUFFER_CONTEXT,
15035                cx,
15036            );
15037        }
15038        multibuffer
15039    });
15040
15041    let editor = cx.add_window(|window, cx| {
15042        Editor::new(
15043            EditorMode::Full,
15044            multibuffer,
15045            Some(project),
15046            true,
15047            window,
15048            cx,
15049        )
15050    });
15051    cx.run_until_parked();
15052
15053    let snapshot = editor
15054        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15055        .unwrap();
15056    let hunks = snapshot
15057        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15058        .map(|hunk| match hunk {
15059            DisplayDiffHunk::Unfolded {
15060                display_row_range, ..
15061            } => display_row_range,
15062            DisplayDiffHunk::Folded { .. } => unreachable!(),
15063        })
15064        .collect::<Vec<_>>();
15065    assert_eq!(
15066        hunks,
15067        [
15068            DisplayRow(3)..DisplayRow(5),
15069            DisplayRow(10)..DisplayRow(12),
15070            DisplayRow(17)..DisplayRow(19),
15071        ]
15072    );
15073}
15074
15075#[gpui::test]
15076async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15077    init_test(cx, |_| {});
15078
15079    let mut cx = EditorTestContext::new(cx).await;
15080    cx.set_head_text(indoc! { "
15081        one
15082        two
15083        three
15084        four
15085        five
15086        "
15087    });
15088    cx.set_index_text(indoc! { "
15089        one
15090        two
15091        three
15092        four
15093        five
15094        "
15095    });
15096    cx.set_state(indoc! {"
15097        one
15098        TWO
15099        ˇTHREE
15100        FOUR
15101        five
15102    "});
15103    cx.run_until_parked();
15104    cx.update_editor(|editor, window, cx| {
15105        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15106    });
15107    cx.run_until_parked();
15108    cx.assert_index_text(Some(indoc! {"
15109        one
15110        TWO
15111        THREE
15112        FOUR
15113        five
15114    "}));
15115    cx.set_state(indoc! { "
15116        one
15117        TWO
15118        ˇTHREE-HUNDRED
15119        FOUR
15120        five
15121    "});
15122    cx.run_until_parked();
15123    cx.update_editor(|editor, window, cx| {
15124        let snapshot = editor.snapshot(window, cx);
15125        let hunks = editor
15126            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15127            .collect::<Vec<_>>();
15128        assert_eq!(hunks.len(), 1);
15129        assert_eq!(
15130            hunks[0].status(),
15131            DiffHunkStatus {
15132                kind: DiffHunkStatusKind::Modified,
15133                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15134            }
15135        );
15136
15137        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15138    });
15139    cx.run_until_parked();
15140    cx.assert_index_text(Some(indoc! {"
15141        one
15142        TWO
15143        THREE-HUNDRED
15144        FOUR
15145        five
15146    "}));
15147}
15148
15149#[gpui::test]
15150fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15151    init_test(cx, |_| {});
15152
15153    let editor = cx.add_window(|window, cx| {
15154        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15155        build_editor(buffer, window, cx)
15156    });
15157
15158    let render_args = Arc::new(Mutex::new(None));
15159    let snapshot = editor
15160        .update(cx, |editor, window, cx| {
15161            let snapshot = editor.buffer().read(cx).snapshot(cx);
15162            let range =
15163                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15164
15165            struct RenderArgs {
15166                row: MultiBufferRow,
15167                folded: bool,
15168                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15169            }
15170
15171            let crease = Crease::inline(
15172                range,
15173                FoldPlaceholder::test(),
15174                {
15175                    let toggle_callback = render_args.clone();
15176                    move |row, folded, callback, _window, _cx| {
15177                        *toggle_callback.lock() = Some(RenderArgs {
15178                            row,
15179                            folded,
15180                            callback,
15181                        });
15182                        div()
15183                    }
15184                },
15185                |_row, _folded, _window, _cx| div(),
15186            );
15187
15188            editor.insert_creases(Some(crease), cx);
15189            let snapshot = editor.snapshot(window, cx);
15190            let _div = snapshot.render_crease_toggle(
15191                MultiBufferRow(1),
15192                false,
15193                cx.entity().clone(),
15194                window,
15195                cx,
15196            );
15197            snapshot
15198        })
15199        .unwrap();
15200
15201    let render_args = render_args.lock().take().unwrap();
15202    assert_eq!(render_args.row, MultiBufferRow(1));
15203    assert!(!render_args.folded);
15204    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15205
15206    cx.update_window(*editor, |_, window, cx| {
15207        (render_args.callback)(true, window, cx)
15208    })
15209    .unwrap();
15210    let snapshot = editor
15211        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15212        .unwrap();
15213    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15214
15215    cx.update_window(*editor, |_, window, cx| {
15216        (render_args.callback)(false, window, cx)
15217    })
15218    .unwrap();
15219    let snapshot = editor
15220        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15221        .unwrap();
15222    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15223}
15224
15225#[gpui::test]
15226async fn test_input_text(cx: &mut TestAppContext) {
15227    init_test(cx, |_| {});
15228    let mut cx = EditorTestContext::new(cx).await;
15229
15230    cx.set_state(
15231        &r#"ˇone
15232        two
15233
15234        three
15235        fourˇ
15236        five
15237
15238        siˇx"#
15239            .unindent(),
15240    );
15241
15242    cx.dispatch_action(HandleInput(String::new()));
15243    cx.assert_editor_state(
15244        &r#"ˇone
15245        two
15246
15247        three
15248        fourˇ
15249        five
15250
15251        siˇx"#
15252            .unindent(),
15253    );
15254
15255    cx.dispatch_action(HandleInput("AAAA".to_string()));
15256    cx.assert_editor_state(
15257        &r#"AAAAˇone
15258        two
15259
15260        three
15261        fourAAAAˇ
15262        five
15263
15264        siAAAAˇx"#
15265            .unindent(),
15266    );
15267}
15268
15269#[gpui::test]
15270async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15271    init_test(cx, |_| {});
15272
15273    let mut cx = EditorTestContext::new(cx).await;
15274    cx.set_state(
15275        r#"let foo = 1;
15276let foo = 2;
15277let foo = 3;
15278let fooˇ = 4;
15279let foo = 5;
15280let foo = 6;
15281let foo = 7;
15282let foo = 8;
15283let foo = 9;
15284let foo = 10;
15285let foo = 11;
15286let foo = 12;
15287let foo = 13;
15288let foo = 14;
15289let foo = 15;"#,
15290    );
15291
15292    cx.update_editor(|e, window, cx| {
15293        assert_eq!(
15294            e.next_scroll_position,
15295            NextScrollCursorCenterTopBottom::Center,
15296            "Default next scroll direction is center",
15297        );
15298
15299        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15300        assert_eq!(
15301            e.next_scroll_position,
15302            NextScrollCursorCenterTopBottom::Top,
15303            "After center, next scroll direction should be top",
15304        );
15305
15306        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15307        assert_eq!(
15308            e.next_scroll_position,
15309            NextScrollCursorCenterTopBottom::Bottom,
15310            "After top, next scroll direction should be bottom",
15311        );
15312
15313        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15314        assert_eq!(
15315            e.next_scroll_position,
15316            NextScrollCursorCenterTopBottom::Center,
15317            "After bottom, scrolling should start over",
15318        );
15319
15320        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15321        assert_eq!(
15322            e.next_scroll_position,
15323            NextScrollCursorCenterTopBottom::Top,
15324            "Scrolling continues if retriggered fast enough"
15325        );
15326    });
15327
15328    cx.executor()
15329        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15330    cx.executor().run_until_parked();
15331    cx.update_editor(|e, _, _| {
15332        assert_eq!(
15333            e.next_scroll_position,
15334            NextScrollCursorCenterTopBottom::Center,
15335            "If scrolling is not triggered fast enough, it should reset"
15336        );
15337    });
15338}
15339
15340#[gpui::test]
15341async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15342    init_test(cx, |_| {});
15343    let mut cx = EditorLspTestContext::new_rust(
15344        lsp::ServerCapabilities {
15345            definition_provider: Some(lsp::OneOf::Left(true)),
15346            references_provider: Some(lsp::OneOf::Left(true)),
15347            ..lsp::ServerCapabilities::default()
15348        },
15349        cx,
15350    )
15351    .await;
15352
15353    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15354        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15355            move |params, _| async move {
15356                if empty_go_to_definition {
15357                    Ok(None)
15358                } else {
15359                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15360                        uri: params.text_document_position_params.text_document.uri,
15361                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15362                    })))
15363                }
15364            },
15365        );
15366        let references =
15367            cx.lsp
15368                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15369                    Ok(Some(vec![lsp::Location {
15370                        uri: params.text_document_position.text_document.uri,
15371                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15372                    }]))
15373                });
15374        (go_to_definition, references)
15375    };
15376
15377    cx.set_state(
15378        &r#"fn one() {
15379            let mut a = ˇtwo();
15380        }
15381
15382        fn two() {}"#
15383            .unindent(),
15384    );
15385    set_up_lsp_handlers(false, &mut cx);
15386    let navigated = cx
15387        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15388        .await
15389        .expect("Failed to navigate to definition");
15390    assert_eq!(
15391        navigated,
15392        Navigated::Yes,
15393        "Should have navigated to definition from the GetDefinition response"
15394    );
15395    cx.assert_editor_state(
15396        &r#"fn one() {
15397            let mut a = two();
15398        }
15399
15400        fn «twoˇ»() {}"#
15401            .unindent(),
15402    );
15403
15404    let editors = cx.update_workspace(|workspace, _, cx| {
15405        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15406    });
15407    cx.update_editor(|_, _, test_editor_cx| {
15408        assert_eq!(
15409            editors.len(),
15410            1,
15411            "Initially, only one, test, editor should be open in the workspace"
15412        );
15413        assert_eq!(
15414            test_editor_cx.entity(),
15415            editors.last().expect("Asserted len is 1").clone()
15416        );
15417    });
15418
15419    set_up_lsp_handlers(true, &mut cx);
15420    let navigated = cx
15421        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15422        .await
15423        .expect("Failed to navigate to lookup references");
15424    assert_eq!(
15425        navigated,
15426        Navigated::Yes,
15427        "Should have navigated to references as a fallback after empty GoToDefinition response"
15428    );
15429    // We should not change the selections in the existing file,
15430    // if opening another milti buffer with the references
15431    cx.assert_editor_state(
15432        &r#"fn one() {
15433            let mut a = two();
15434        }
15435
15436        fn «twoˇ»() {}"#
15437            .unindent(),
15438    );
15439    let editors = cx.update_workspace(|workspace, _, cx| {
15440        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15441    });
15442    cx.update_editor(|_, _, test_editor_cx| {
15443        assert_eq!(
15444            editors.len(),
15445            2,
15446            "After falling back to references search, we open a new editor with the results"
15447        );
15448        let references_fallback_text = editors
15449            .into_iter()
15450            .find(|new_editor| *new_editor != test_editor_cx.entity())
15451            .expect("Should have one non-test editor now")
15452            .read(test_editor_cx)
15453            .text(test_editor_cx);
15454        assert_eq!(
15455            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15456            "Should use the range from the references response and not the GoToDefinition one"
15457        );
15458    });
15459}
15460
15461#[gpui::test]
15462async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15463    init_test(cx, |_| {});
15464
15465    let language = Arc::new(Language::new(
15466        LanguageConfig::default(),
15467        Some(tree_sitter_rust::LANGUAGE.into()),
15468    ));
15469
15470    let text = r#"
15471        #[cfg(test)]
15472        mod tests() {
15473            #[test]
15474            fn runnable_1() {
15475                let a = 1;
15476            }
15477
15478            #[test]
15479            fn runnable_2() {
15480                let a = 1;
15481                let b = 2;
15482            }
15483        }
15484    "#
15485    .unindent();
15486
15487    let fs = FakeFs::new(cx.executor());
15488    fs.insert_file("/file.rs", Default::default()).await;
15489
15490    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15491    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15492    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15493    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15494    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15495
15496    let editor = cx.new_window_entity(|window, cx| {
15497        Editor::new(
15498            EditorMode::Full,
15499            multi_buffer,
15500            Some(project.clone()),
15501            true,
15502            window,
15503            cx,
15504        )
15505    });
15506
15507    editor.update_in(cx, |editor, window, cx| {
15508        let snapshot = editor.buffer().read(cx).snapshot(cx);
15509        editor.tasks.insert(
15510            (buffer.read(cx).remote_id(), 3),
15511            RunnableTasks {
15512                templates: vec![],
15513                offset: snapshot.anchor_before(43),
15514                column: 0,
15515                extra_variables: HashMap::default(),
15516                context_range: BufferOffset(43)..BufferOffset(85),
15517            },
15518        );
15519        editor.tasks.insert(
15520            (buffer.read(cx).remote_id(), 8),
15521            RunnableTasks {
15522                templates: vec![],
15523                offset: snapshot.anchor_before(86),
15524                column: 0,
15525                extra_variables: HashMap::default(),
15526                context_range: BufferOffset(86)..BufferOffset(191),
15527            },
15528        );
15529
15530        // Test finding task when cursor is inside function body
15531        editor.change_selections(None, window, cx, |s| {
15532            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15533        });
15534        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15535        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15536
15537        // Test finding task when cursor is on function name
15538        editor.change_selections(None, window, cx, |s| {
15539            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15540        });
15541        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15542        assert_eq!(row, 8, "Should find task when cursor is on function name");
15543    });
15544}
15545
15546#[gpui::test]
15547async fn test_multi_buffer_folding(cx: &mut TestAppContext) {
15548    init_test(cx, |_| {});
15549
15550    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15551    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15552    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15553
15554    let fs = FakeFs::new(cx.executor());
15555    fs.insert_tree(
15556        path!("/a"),
15557        json!({
15558            "first.rs": sample_text_1,
15559            "second.rs": sample_text_2,
15560            "third.rs": sample_text_3,
15561        }),
15562    )
15563    .await;
15564    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15565    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15566    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15567    let worktree = project.update(cx, |project, cx| {
15568        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15569        assert_eq!(worktrees.len(), 1);
15570        worktrees.pop().unwrap()
15571    });
15572    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15573
15574    let buffer_1 = project
15575        .update(cx, |project, cx| {
15576            project.open_buffer((worktree_id, "first.rs"), cx)
15577        })
15578        .await
15579        .unwrap();
15580    let buffer_2 = project
15581        .update(cx, |project, cx| {
15582            project.open_buffer((worktree_id, "second.rs"), cx)
15583        })
15584        .await
15585        .unwrap();
15586    let buffer_3 = project
15587        .update(cx, |project, cx| {
15588            project.open_buffer((worktree_id, "third.rs"), cx)
15589        })
15590        .await
15591        .unwrap();
15592
15593    let multi_buffer = cx.new(|cx| {
15594        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15595        multi_buffer.push_excerpts(
15596            buffer_1.clone(),
15597            [
15598                ExcerptRange {
15599                    context: Point::new(0, 0)..Point::new(3, 0),
15600                    primary: None,
15601                },
15602                ExcerptRange {
15603                    context: Point::new(5, 0)..Point::new(7, 0),
15604                    primary: None,
15605                },
15606                ExcerptRange {
15607                    context: Point::new(9, 0)..Point::new(10, 4),
15608                    primary: None,
15609                },
15610            ],
15611            cx,
15612        );
15613        multi_buffer.push_excerpts(
15614            buffer_2.clone(),
15615            [
15616                ExcerptRange {
15617                    context: Point::new(0, 0)..Point::new(3, 0),
15618                    primary: None,
15619                },
15620                ExcerptRange {
15621                    context: Point::new(5, 0)..Point::new(7, 0),
15622                    primary: None,
15623                },
15624                ExcerptRange {
15625                    context: Point::new(9, 0)..Point::new(10, 4),
15626                    primary: None,
15627                },
15628            ],
15629            cx,
15630        );
15631        multi_buffer.push_excerpts(
15632            buffer_3.clone(),
15633            [
15634                ExcerptRange {
15635                    context: Point::new(0, 0)..Point::new(3, 0),
15636                    primary: None,
15637                },
15638                ExcerptRange {
15639                    context: Point::new(5, 0)..Point::new(7, 0),
15640                    primary: None,
15641                },
15642                ExcerptRange {
15643                    context: Point::new(9, 0)..Point::new(10, 4),
15644                    primary: None,
15645                },
15646            ],
15647            cx,
15648        );
15649        multi_buffer
15650    });
15651    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15652        Editor::new(
15653            EditorMode::Full,
15654            multi_buffer,
15655            Some(project.clone()),
15656            true,
15657            window,
15658            cx,
15659        )
15660    });
15661
15662    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";
15663    assert_eq!(
15664        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15665        full_text,
15666    );
15667
15668    multi_buffer_editor.update(cx, |editor, cx| {
15669        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15670    });
15671    assert_eq!(
15672        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15673        "\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",
15674        "After folding the first buffer, its text should not be displayed"
15675    );
15676
15677    multi_buffer_editor.update(cx, |editor, cx| {
15678        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15679    });
15680    assert_eq!(
15681        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15682        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15683        "After folding the second buffer, its text should not be displayed"
15684    );
15685
15686    multi_buffer_editor.update(cx, |editor, cx| {
15687        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15688    });
15689    assert_eq!(
15690        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15691        "\n\n\n\n\n",
15692        "After folding the third buffer, its text should not be displayed"
15693    );
15694
15695    // Emulate selection inside the fold logic, that should work
15696    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15697        editor
15698            .snapshot(window, cx)
15699            .next_line_boundary(Point::new(0, 4));
15700    });
15701
15702    multi_buffer_editor.update(cx, |editor, cx| {
15703        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15704    });
15705    assert_eq!(
15706        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15707        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15708        "After unfolding the second buffer, its text should be displayed"
15709    );
15710
15711    multi_buffer_editor.update(cx, |editor, cx| {
15712        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15713    });
15714    assert_eq!(
15715        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15716        "\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",
15717        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15718    );
15719
15720    multi_buffer_editor.update(cx, |editor, cx| {
15721        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15722    });
15723    assert_eq!(
15724        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15725        full_text,
15726        "After unfolding the all buffers, all original text should be displayed"
15727    );
15728}
15729
15730#[gpui::test]
15731async fn test_multi_buffer_single_excerpts_folding(cx: &mut TestAppContext) {
15732    init_test(cx, |_| {});
15733
15734    let sample_text_1 = "1111\n2222\n3333".to_string();
15735    let sample_text_2 = "4444\n5555\n6666".to_string();
15736    let sample_text_3 = "7777\n8888\n9999".to_string();
15737
15738    let fs = FakeFs::new(cx.executor());
15739    fs.insert_tree(
15740        path!("/a"),
15741        json!({
15742            "first.rs": sample_text_1,
15743            "second.rs": sample_text_2,
15744            "third.rs": sample_text_3,
15745        }),
15746    )
15747    .await;
15748    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15749    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15750    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15751    let worktree = project.update(cx, |project, cx| {
15752        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15753        assert_eq!(worktrees.len(), 1);
15754        worktrees.pop().unwrap()
15755    });
15756    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15757
15758    let buffer_1 = project
15759        .update(cx, |project, cx| {
15760            project.open_buffer((worktree_id, "first.rs"), cx)
15761        })
15762        .await
15763        .unwrap();
15764    let buffer_2 = project
15765        .update(cx, |project, cx| {
15766            project.open_buffer((worktree_id, "second.rs"), cx)
15767        })
15768        .await
15769        .unwrap();
15770    let buffer_3 = project
15771        .update(cx, |project, cx| {
15772            project.open_buffer((worktree_id, "third.rs"), cx)
15773        })
15774        .await
15775        .unwrap();
15776
15777    let multi_buffer = cx.new(|cx| {
15778        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15779        multi_buffer.push_excerpts(
15780            buffer_1.clone(),
15781            [ExcerptRange {
15782                context: Point::new(0, 0)..Point::new(3, 0),
15783                primary: None,
15784            }],
15785            cx,
15786        );
15787        multi_buffer.push_excerpts(
15788            buffer_2.clone(),
15789            [ExcerptRange {
15790                context: Point::new(0, 0)..Point::new(3, 0),
15791                primary: None,
15792            }],
15793            cx,
15794        );
15795        multi_buffer.push_excerpts(
15796            buffer_3.clone(),
15797            [ExcerptRange {
15798                context: Point::new(0, 0)..Point::new(3, 0),
15799                primary: None,
15800            }],
15801            cx,
15802        );
15803        multi_buffer
15804    });
15805
15806    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15807        Editor::new(
15808            EditorMode::Full,
15809            multi_buffer,
15810            Some(project.clone()),
15811            true,
15812            window,
15813            cx,
15814        )
15815    });
15816
15817    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15818    assert_eq!(
15819        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15820        full_text,
15821    );
15822
15823    multi_buffer_editor.update(cx, |editor, cx| {
15824        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15825    });
15826    assert_eq!(
15827        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15828        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15829        "After folding the first buffer, its text should not be displayed"
15830    );
15831
15832    multi_buffer_editor.update(cx, |editor, cx| {
15833        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15834    });
15835
15836    assert_eq!(
15837        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15838        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15839        "After folding the second buffer, its text should not be displayed"
15840    );
15841
15842    multi_buffer_editor.update(cx, |editor, cx| {
15843        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15844    });
15845    assert_eq!(
15846        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15847        "\n\n\n\n\n",
15848        "After folding the third buffer, its text should not be displayed"
15849    );
15850
15851    multi_buffer_editor.update(cx, |editor, cx| {
15852        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15853    });
15854    assert_eq!(
15855        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15856        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15857        "After unfolding the second buffer, its text should be displayed"
15858    );
15859
15860    multi_buffer_editor.update(cx, |editor, cx| {
15861        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15862    });
15863    assert_eq!(
15864        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15865        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15866        "After unfolding the first buffer, its text should be displayed"
15867    );
15868
15869    multi_buffer_editor.update(cx, |editor, cx| {
15870        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15871    });
15872    assert_eq!(
15873        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15874        full_text,
15875        "After unfolding all buffers, all original text should be displayed"
15876    );
15877}
15878
15879#[gpui::test]
15880async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut TestAppContext) {
15881    init_test(cx, |_| {});
15882
15883    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15884
15885    let fs = FakeFs::new(cx.executor());
15886    fs.insert_tree(
15887        path!("/a"),
15888        json!({
15889            "main.rs": sample_text,
15890        }),
15891    )
15892    .await;
15893    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15894    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15895    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15896    let worktree = project.update(cx, |project, cx| {
15897        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15898        assert_eq!(worktrees.len(), 1);
15899        worktrees.pop().unwrap()
15900    });
15901    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15902
15903    let buffer_1 = project
15904        .update(cx, |project, cx| {
15905            project.open_buffer((worktree_id, "main.rs"), cx)
15906        })
15907        .await
15908        .unwrap();
15909
15910    let multi_buffer = cx.new(|cx| {
15911        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15912        multi_buffer.push_excerpts(
15913            buffer_1.clone(),
15914            [ExcerptRange {
15915                context: Point::new(0, 0)
15916                    ..Point::new(
15917                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15918                        0,
15919                    ),
15920                primary: None,
15921            }],
15922            cx,
15923        );
15924        multi_buffer
15925    });
15926    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15927        Editor::new(
15928            EditorMode::Full,
15929            multi_buffer,
15930            Some(project.clone()),
15931            true,
15932            window,
15933            cx,
15934        )
15935    });
15936
15937    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15938    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15939        enum TestHighlight {}
15940        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15941        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15942        editor.highlight_text::<TestHighlight>(
15943            vec![highlight_range.clone()],
15944            HighlightStyle::color(Hsla::green()),
15945            cx,
15946        );
15947        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15948    });
15949
15950    let full_text = format!("\n\n\n{sample_text}\n");
15951    assert_eq!(
15952        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15953        full_text,
15954    );
15955}
15956
15957#[gpui::test]
15958async fn test_inline_completion_text(cx: &mut TestAppContext) {
15959    init_test(cx, |_| {});
15960
15961    // Simple insertion
15962    assert_highlighted_edits(
15963        "Hello, world!",
15964        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15965        true,
15966        cx,
15967        |highlighted_edits, cx| {
15968            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15969            assert_eq!(highlighted_edits.highlights.len(), 1);
15970            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15971            assert_eq!(
15972                highlighted_edits.highlights[0].1.background_color,
15973                Some(cx.theme().status().created_background)
15974            );
15975        },
15976    )
15977    .await;
15978
15979    // Replacement
15980    assert_highlighted_edits(
15981        "This is a test.",
15982        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15983        false,
15984        cx,
15985        |highlighted_edits, cx| {
15986            assert_eq!(highlighted_edits.text, "That is a test.");
15987            assert_eq!(highlighted_edits.highlights.len(), 1);
15988            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15989            assert_eq!(
15990                highlighted_edits.highlights[0].1.background_color,
15991                Some(cx.theme().status().created_background)
15992            );
15993        },
15994    )
15995    .await;
15996
15997    // Multiple edits
15998    assert_highlighted_edits(
15999        "Hello, world!",
16000        vec![
16001            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16002            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16003        ],
16004        false,
16005        cx,
16006        |highlighted_edits, cx| {
16007            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16008            assert_eq!(highlighted_edits.highlights.len(), 2);
16009            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16010            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16011            assert_eq!(
16012                highlighted_edits.highlights[0].1.background_color,
16013                Some(cx.theme().status().created_background)
16014            );
16015            assert_eq!(
16016                highlighted_edits.highlights[1].1.background_color,
16017                Some(cx.theme().status().created_background)
16018            );
16019        },
16020    )
16021    .await;
16022
16023    // Multiple lines with edits
16024    assert_highlighted_edits(
16025        "First line\nSecond line\nThird line\nFourth line",
16026        vec![
16027            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16028            (
16029                Point::new(2, 0)..Point::new(2, 10),
16030                "New third line".to_string(),
16031            ),
16032            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16033        ],
16034        false,
16035        cx,
16036        |highlighted_edits, cx| {
16037            assert_eq!(
16038                highlighted_edits.text,
16039                "Second modified\nNew third line\nFourth updated line"
16040            );
16041            assert_eq!(highlighted_edits.highlights.len(), 3);
16042            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16043            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16044            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16045            for highlight in &highlighted_edits.highlights {
16046                assert_eq!(
16047                    highlight.1.background_color,
16048                    Some(cx.theme().status().created_background)
16049                );
16050            }
16051        },
16052    )
16053    .await;
16054}
16055
16056#[gpui::test]
16057async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16058    init_test(cx, |_| {});
16059
16060    // Deletion
16061    assert_highlighted_edits(
16062        "Hello, world!",
16063        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16064        true,
16065        cx,
16066        |highlighted_edits, cx| {
16067            assert_eq!(highlighted_edits.text, "Hello, world!");
16068            assert_eq!(highlighted_edits.highlights.len(), 1);
16069            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16070            assert_eq!(
16071                highlighted_edits.highlights[0].1.background_color,
16072                Some(cx.theme().status().deleted_background)
16073            );
16074        },
16075    )
16076    .await;
16077
16078    // Insertion
16079    assert_highlighted_edits(
16080        "Hello, world!",
16081        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16082        true,
16083        cx,
16084        |highlighted_edits, cx| {
16085            assert_eq!(highlighted_edits.highlights.len(), 1);
16086            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16087            assert_eq!(
16088                highlighted_edits.highlights[0].1.background_color,
16089                Some(cx.theme().status().created_background)
16090            );
16091        },
16092    )
16093    .await;
16094}
16095
16096async fn assert_highlighted_edits(
16097    text: &str,
16098    edits: Vec<(Range<Point>, String)>,
16099    include_deletions: bool,
16100    cx: &mut TestAppContext,
16101    assertion_fn: impl Fn(HighlightedText, &App),
16102) {
16103    let window = cx.add_window(|window, cx| {
16104        let buffer = MultiBuffer::build_simple(text, cx);
16105        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16106    });
16107    let cx = &mut VisualTestContext::from_window(*window, cx);
16108
16109    let (buffer, snapshot) = window
16110        .update(cx, |editor, _window, cx| {
16111            (
16112                editor.buffer().clone(),
16113                editor.buffer().read(cx).snapshot(cx),
16114            )
16115        })
16116        .unwrap();
16117
16118    let edits = edits
16119        .into_iter()
16120        .map(|(range, edit)| {
16121            (
16122                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16123                edit,
16124            )
16125        })
16126        .collect::<Vec<_>>();
16127
16128    let text_anchor_edits = edits
16129        .clone()
16130        .into_iter()
16131        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16132        .collect::<Vec<_>>();
16133
16134    let edit_preview = window
16135        .update(cx, |_, _window, cx| {
16136            buffer
16137                .read(cx)
16138                .as_singleton()
16139                .unwrap()
16140                .read(cx)
16141                .preview_edits(text_anchor_edits.into(), cx)
16142        })
16143        .unwrap()
16144        .await;
16145
16146    cx.update(|_window, cx| {
16147        let highlighted_edits = inline_completion_edit_text(
16148            &snapshot.as_singleton().unwrap().2,
16149            &edits,
16150            &edit_preview,
16151            include_deletions,
16152            cx,
16153        );
16154        assertion_fn(highlighted_edits, cx)
16155    });
16156}
16157
16158#[gpui::test]
16159async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16160    init_test(cx, |_| {});
16161    let capabilities = lsp::ServerCapabilities {
16162        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16163            prepare_provider: Some(true),
16164            work_done_progress_options: Default::default(),
16165        })),
16166        ..Default::default()
16167    };
16168    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16169
16170    cx.set_state(indoc! {"
16171        struct Fˇoo {}
16172    "});
16173
16174    cx.update_editor(|editor, _, cx| {
16175        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16176        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16177        editor.highlight_background::<DocumentHighlightRead>(
16178            &[highlight_range],
16179            |c| c.editor_document_highlight_read_background,
16180            cx,
16181        );
16182    });
16183
16184    let mut prepare_rename_handler =
16185        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16186            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16187                start: lsp::Position {
16188                    line: 0,
16189                    character: 7,
16190                },
16191                end: lsp::Position {
16192                    line: 0,
16193                    character: 10,
16194                },
16195            })))
16196        });
16197    let prepare_rename_task = cx
16198        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16199        .expect("Prepare rename was not started");
16200    prepare_rename_handler.next().await.unwrap();
16201    prepare_rename_task.await.expect("Prepare rename failed");
16202
16203    let mut rename_handler =
16204        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16205            let edit = lsp::TextEdit {
16206                range: lsp::Range {
16207                    start: lsp::Position {
16208                        line: 0,
16209                        character: 7,
16210                    },
16211                    end: lsp::Position {
16212                        line: 0,
16213                        character: 10,
16214                    },
16215                },
16216                new_text: "FooRenamed".to_string(),
16217            };
16218            Ok(Some(lsp::WorkspaceEdit::new(
16219                // Specify the same edit twice
16220                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16221            )))
16222        });
16223    let rename_task = cx
16224        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16225        .expect("Confirm rename was not started");
16226    rename_handler.next().await.unwrap();
16227    rename_task.await.expect("Confirm rename failed");
16228    cx.run_until_parked();
16229
16230    // Despite two edits, only one is actually applied as those are identical
16231    cx.assert_editor_state(indoc! {"
16232        struct FooRenamedˇ {}
16233    "});
16234}
16235
16236#[gpui::test]
16237async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16238    init_test(cx, |_| {});
16239    // These capabilities indicate that the server does not support prepare rename.
16240    let capabilities = lsp::ServerCapabilities {
16241        rename_provider: Some(lsp::OneOf::Left(true)),
16242        ..Default::default()
16243    };
16244    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16245
16246    cx.set_state(indoc! {"
16247        struct Fˇoo {}
16248    "});
16249
16250    cx.update_editor(|editor, _window, cx| {
16251        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16252        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16253        editor.highlight_background::<DocumentHighlightRead>(
16254            &[highlight_range],
16255            |c| c.editor_document_highlight_read_background,
16256            cx,
16257        );
16258    });
16259
16260    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16261        .expect("Prepare rename was not started")
16262        .await
16263        .expect("Prepare rename failed");
16264
16265    let mut rename_handler =
16266        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16267            let edit = lsp::TextEdit {
16268                range: lsp::Range {
16269                    start: lsp::Position {
16270                        line: 0,
16271                        character: 7,
16272                    },
16273                    end: lsp::Position {
16274                        line: 0,
16275                        character: 10,
16276                    },
16277                },
16278                new_text: "FooRenamed".to_string(),
16279            };
16280            Ok(Some(lsp::WorkspaceEdit::new(
16281                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16282            )))
16283        });
16284    let rename_task = cx
16285        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16286        .expect("Confirm rename was not started");
16287    rename_handler.next().await.unwrap();
16288    rename_task.await.expect("Confirm rename failed");
16289    cx.run_until_parked();
16290
16291    // Correct range is renamed, as `surrounding_word` is used to find it.
16292    cx.assert_editor_state(indoc! {"
16293        struct FooRenamedˇ {}
16294    "});
16295}
16296
16297#[gpui::test]
16298async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16299    init_test(cx, |_| {});
16300    let mut cx = EditorTestContext::new(cx).await;
16301
16302    let language = Arc::new(
16303        Language::new(
16304            LanguageConfig::default(),
16305            Some(tree_sitter_html::LANGUAGE.into()),
16306        )
16307        .with_brackets_query(
16308            r#"
16309            ("<" @open "/>" @close)
16310            ("</" @open ">" @close)
16311            ("<" @open ">" @close)
16312            ("\"" @open "\"" @close)
16313            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16314        "#,
16315        )
16316        .unwrap(),
16317    );
16318    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16319
16320    cx.set_state(indoc! {"
16321        <span>ˇ</span>
16322    "});
16323    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16324    cx.assert_editor_state(indoc! {"
16325        <span>
16326        ˇ
16327        </span>
16328    "});
16329
16330    cx.set_state(indoc! {"
16331        <span><span></span>ˇ</span>
16332    "});
16333    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16334    cx.assert_editor_state(indoc! {"
16335        <span><span></span>
16336        ˇ</span>
16337    "});
16338
16339    cx.set_state(indoc! {"
16340        <span>ˇ
16341        </span>
16342    "});
16343    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16344    cx.assert_editor_state(indoc! {"
16345        <span>
16346        ˇ
16347        </span>
16348    "});
16349}
16350
16351fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16352    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16353    point..point
16354}
16355
16356fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16357    let (text, ranges) = marked_text_ranges(marked_text, true);
16358    assert_eq!(editor.text(cx), text);
16359    assert_eq!(
16360        editor.selections.ranges(cx),
16361        ranges,
16362        "Assert selections are {}",
16363        marked_text
16364    );
16365}
16366
16367pub fn handle_signature_help_request(
16368    cx: &mut EditorLspTestContext,
16369    mocked_response: lsp::SignatureHelp,
16370) -> impl Future<Output = ()> {
16371    let mut request =
16372        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16373            let mocked_response = mocked_response.clone();
16374            async move { Ok(Some(mocked_response)) }
16375        });
16376
16377    async move {
16378        request.next().await;
16379    }
16380}
16381
16382/// Handle completion request passing a marked string specifying where the completion
16383/// should be triggered from using '|' character, what range should be replaced, and what completions
16384/// should be returned using '<' and '>' to delimit the range
16385pub fn handle_completion_request(
16386    cx: &mut EditorLspTestContext,
16387    marked_string: &str,
16388    completions: Vec<&'static str>,
16389    counter: Arc<AtomicUsize>,
16390) -> impl Future<Output = ()> {
16391    let complete_from_marker: TextRangeMarker = '|'.into();
16392    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16393    let (_, mut marked_ranges) = marked_text_ranges_by(
16394        marked_string,
16395        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16396    );
16397
16398    let complete_from_position =
16399        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16400    let replace_range =
16401        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16402
16403    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16404        let completions = completions.clone();
16405        counter.fetch_add(1, atomic::Ordering::Release);
16406        async move {
16407            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16408            assert_eq!(
16409                params.text_document_position.position,
16410                complete_from_position
16411            );
16412            Ok(Some(lsp::CompletionResponse::Array(
16413                completions
16414                    .iter()
16415                    .map(|completion_text| lsp::CompletionItem {
16416                        label: completion_text.to_string(),
16417                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16418                            range: replace_range,
16419                            new_text: completion_text.to_string(),
16420                        })),
16421                        ..Default::default()
16422                    })
16423                    .collect(),
16424            )))
16425        }
16426    });
16427
16428    async move {
16429        request.next().await;
16430    }
16431}
16432
16433fn handle_resolve_completion_request(
16434    cx: &mut EditorLspTestContext,
16435    edits: Option<Vec<(&'static str, &'static str)>>,
16436) -> impl Future<Output = ()> {
16437    let edits = edits.map(|edits| {
16438        edits
16439            .iter()
16440            .map(|(marked_string, new_text)| {
16441                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16442                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16443                lsp::TextEdit::new(replace_range, new_text.to_string())
16444            })
16445            .collect::<Vec<_>>()
16446    });
16447
16448    let mut request =
16449        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16450            let edits = edits.clone();
16451            async move {
16452                Ok(lsp::CompletionItem {
16453                    additional_text_edits: edits,
16454                    ..Default::default()
16455                })
16456            }
16457        });
16458
16459    async move {
16460        request.next().await;
16461    }
16462}
16463
16464pub(crate) fn update_test_language_settings(
16465    cx: &mut TestAppContext,
16466    f: impl Fn(&mut AllLanguageSettingsContent),
16467) {
16468    cx.update(|cx| {
16469        SettingsStore::update_global(cx, |store, cx| {
16470            store.update_user_settings::<AllLanguageSettings>(cx, f);
16471        });
16472    });
16473}
16474
16475pub(crate) fn update_test_project_settings(
16476    cx: &mut TestAppContext,
16477    f: impl Fn(&mut ProjectSettings),
16478) {
16479    cx.update(|cx| {
16480        SettingsStore::update_global(cx, |store, cx| {
16481            store.update_user_settings::<ProjectSettings>(cx, f);
16482        });
16483    });
16484}
16485
16486pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16487    cx.update(|cx| {
16488        assets::Assets.load_test_fonts(cx);
16489        let store = SettingsStore::test(cx);
16490        cx.set_global(store);
16491        theme::init(theme::LoadThemes::JustBase, cx);
16492        release_channel::init(SemanticVersion::default(), cx);
16493        client::init_settings(cx);
16494        language::init(cx);
16495        Project::init_settings(cx);
16496        workspace::init_settings(cx);
16497        crate::init(cx);
16498    });
16499
16500    update_test_language_settings(cx, f);
16501}
16502
16503#[track_caller]
16504fn assert_hunk_revert(
16505    not_reverted_text_with_selections: &str,
16506    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16507    expected_reverted_text_with_selections: &str,
16508    base_text: &str,
16509    cx: &mut EditorLspTestContext,
16510) {
16511    cx.set_state(not_reverted_text_with_selections);
16512    cx.set_head_text(base_text);
16513    cx.clear_index_text();
16514    cx.executor().run_until_parked();
16515
16516    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16517        let snapshot = editor.snapshot(window, cx);
16518        let reverted_hunk_statuses = snapshot
16519            .buffer_snapshot
16520            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16521            .map(|hunk| hunk.status())
16522            .collect::<Vec<_>>();
16523
16524        editor.git_restore(&Default::default(), window, cx);
16525        reverted_hunk_statuses
16526    });
16527    cx.executor().run_until_parked();
16528    cx.assert_editor_state(expected_reverted_text_with_selections);
16529    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16530}