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