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_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 7880    init_test(cx, |settings| {
 7881        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7882            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7883        ))
 7884    });
 7885
 7886    let fs = FakeFs::new(cx.executor());
 7887    fs.insert_file(path!("/file.ts"), Default::default()).await;
 7888
 7889    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7890
 7891    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7892    language_registry.add(Arc::new(Language::new(
 7893        LanguageConfig {
 7894            name: "TypeScript".into(),
 7895            matcher: LanguageMatcher {
 7896                path_suffixes: vec!["ts".to_string()],
 7897                ..Default::default()
 7898            },
 7899            ..LanguageConfig::default()
 7900        },
 7901        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 7902    )));
 7903    update_test_language_settings(cx, |settings| {
 7904        settings.defaults.prettier = Some(PrettierSettings {
 7905            allowed: true,
 7906            ..PrettierSettings::default()
 7907        });
 7908    });
 7909    let mut fake_servers = language_registry.register_fake_lsp(
 7910        "TypeScript",
 7911        FakeLspAdapter {
 7912            capabilities: lsp::ServerCapabilities {
 7913                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 7914                ..Default::default()
 7915            },
 7916            ..Default::default()
 7917        },
 7918    );
 7919
 7920    let buffer = project
 7921        .update(cx, |project, cx| {
 7922            project.open_local_buffer(path!("/file.ts"), cx)
 7923        })
 7924        .await
 7925        .unwrap();
 7926
 7927    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7928    let (editor, cx) = cx.add_window_view(|window, cx| {
 7929        build_editor_with_project(project.clone(), buffer, window, cx)
 7930    });
 7931    editor.update_in(cx, |editor, window, cx| {
 7932        editor.set_text(
 7933            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 7934            window,
 7935            cx,
 7936        )
 7937    });
 7938
 7939    cx.executor().start_waiting();
 7940    let fake_server = fake_servers.next().await.unwrap();
 7941
 7942    let format = editor
 7943        .update_in(cx, |editor, window, cx| {
 7944            editor.perform_code_action_kind(
 7945                project.clone(),
 7946                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 7947                window,
 7948                cx,
 7949            )
 7950        })
 7951        .unwrap();
 7952    fake_server
 7953        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 7954            assert_eq!(
 7955                params.text_document.uri,
 7956                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 7957            );
 7958            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 7959                lsp::CodeAction {
 7960                    title: "Organize Imports".to_string(),
 7961                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 7962                    edit: Some(lsp::WorkspaceEdit {
 7963                        changes: Some(
 7964                            [(
 7965                                params.text_document.uri.clone(),
 7966                                vec![lsp::TextEdit::new(
 7967                                    lsp::Range::new(
 7968                                        lsp::Position::new(1, 0),
 7969                                        lsp::Position::new(2, 0),
 7970                                    ),
 7971                                    "".to_string(),
 7972                                )],
 7973                            )]
 7974                            .into_iter()
 7975                            .collect(),
 7976                        ),
 7977                        ..Default::default()
 7978                    }),
 7979                    ..Default::default()
 7980                },
 7981            )]))
 7982        })
 7983        .next()
 7984        .await;
 7985    cx.executor().start_waiting();
 7986    format.await;
 7987    assert_eq!(
 7988        editor.update(cx, |editor, cx| editor.text(cx)),
 7989        "import { a } from 'module';\n\nconst x = a;\n"
 7990    );
 7991
 7992    editor.update_in(cx, |editor, window, cx| {
 7993        editor.set_text(
 7994            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 7995            window,
 7996            cx,
 7997        )
 7998    });
 7999    // Ensure we don't lock if code action hangs.
 8000    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8001        move |params, _| async move {
 8002            assert_eq!(
 8003                params.text_document.uri,
 8004                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8005            );
 8006            futures::future::pending::<()>().await;
 8007            unreachable!()
 8008        },
 8009    );
 8010    let format = editor
 8011        .update_in(cx, |editor, window, cx| {
 8012            editor.perform_code_action_kind(
 8013                project,
 8014                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8015                window,
 8016                cx,
 8017            )
 8018        })
 8019        .unwrap();
 8020    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8021    cx.executor().start_waiting();
 8022    format.await;
 8023    assert_eq!(
 8024        editor.update(cx, |editor, cx| editor.text(cx)),
 8025        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8026    );
 8027}
 8028
 8029#[gpui::test]
 8030async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8031    init_test(cx, |_| {});
 8032
 8033    let mut cx = EditorLspTestContext::new_rust(
 8034        lsp::ServerCapabilities {
 8035            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8036            ..Default::default()
 8037        },
 8038        cx,
 8039    )
 8040    .await;
 8041
 8042    cx.set_state(indoc! {"
 8043        one.twoˇ
 8044    "});
 8045
 8046    // The format request takes a long time. When it completes, it inserts
 8047    // a newline and an indent before the `.`
 8048    cx.lsp
 8049        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8050            let executor = cx.background_executor().clone();
 8051            async move {
 8052                executor.timer(Duration::from_millis(100)).await;
 8053                Ok(Some(vec![lsp::TextEdit {
 8054                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8055                    new_text: "\n    ".into(),
 8056                }]))
 8057            }
 8058        });
 8059
 8060    // Submit a format request.
 8061    let format_1 = cx
 8062        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8063        .unwrap();
 8064    cx.executor().run_until_parked();
 8065
 8066    // Submit a second format request.
 8067    let format_2 = cx
 8068        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8069        .unwrap();
 8070    cx.executor().run_until_parked();
 8071
 8072    // Wait for both format requests to complete
 8073    cx.executor().advance_clock(Duration::from_millis(200));
 8074    cx.executor().start_waiting();
 8075    format_1.await.unwrap();
 8076    cx.executor().start_waiting();
 8077    format_2.await.unwrap();
 8078
 8079    // The formatting edits only happens once.
 8080    cx.assert_editor_state(indoc! {"
 8081        one
 8082            .twoˇ
 8083    "});
 8084}
 8085
 8086#[gpui::test]
 8087async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8088    init_test(cx, |settings| {
 8089        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8090    });
 8091
 8092    let mut cx = EditorLspTestContext::new_rust(
 8093        lsp::ServerCapabilities {
 8094            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8095            ..Default::default()
 8096        },
 8097        cx,
 8098    )
 8099    .await;
 8100
 8101    // Set up a buffer white some trailing whitespace and no trailing newline.
 8102    cx.set_state(
 8103        &[
 8104            "one ",   //
 8105            "twoˇ",   //
 8106            "three ", //
 8107            "four",   //
 8108        ]
 8109        .join("\n"),
 8110    );
 8111
 8112    // Submit a format request.
 8113    let format = cx
 8114        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8115        .unwrap();
 8116
 8117    // Record which buffer changes have been sent to the language server
 8118    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8119    cx.lsp
 8120        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8121            let buffer_changes = buffer_changes.clone();
 8122            move |params, _| {
 8123                buffer_changes.lock().extend(
 8124                    params
 8125                        .content_changes
 8126                        .into_iter()
 8127                        .map(|e| (e.range.unwrap(), e.text)),
 8128                );
 8129            }
 8130        });
 8131
 8132    // Handle formatting requests to the language server.
 8133    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8134        let buffer_changes = buffer_changes.clone();
 8135        move |_, _| {
 8136            // When formatting is requested, trailing whitespace has already been stripped,
 8137            // and the trailing newline has already been added.
 8138            assert_eq!(
 8139                &buffer_changes.lock()[1..],
 8140                &[
 8141                    (
 8142                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8143                        "".into()
 8144                    ),
 8145                    (
 8146                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8147                        "".into()
 8148                    ),
 8149                    (
 8150                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8151                        "\n".into()
 8152                    ),
 8153                ]
 8154            );
 8155
 8156            // Insert blank lines between each line of the buffer.
 8157            async move {
 8158                Ok(Some(vec![
 8159                    lsp::TextEdit {
 8160                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8161                        new_text: "\n".into(),
 8162                    },
 8163                    lsp::TextEdit {
 8164                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8165                        new_text: "\n".into(),
 8166                    },
 8167                ]))
 8168            }
 8169        }
 8170    });
 8171
 8172    // After formatting the buffer, the trailing whitespace is stripped,
 8173    // a newline is appended, and the edits provided by the language server
 8174    // have been applied.
 8175    format.await.unwrap();
 8176    cx.assert_editor_state(
 8177        &[
 8178            "one",   //
 8179            "",      //
 8180            "twoˇ",  //
 8181            "",      //
 8182            "three", //
 8183            "four",  //
 8184            "",      //
 8185        ]
 8186        .join("\n"),
 8187    );
 8188
 8189    // Undoing the formatting undoes the trailing whitespace removal, the
 8190    // trailing newline, and the LSP edits.
 8191    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8192    cx.assert_editor_state(
 8193        &[
 8194            "one ",   //
 8195            "twoˇ",   //
 8196            "three ", //
 8197            "four",   //
 8198        ]
 8199        .join("\n"),
 8200    );
 8201}
 8202
 8203#[gpui::test]
 8204async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8205    cx: &mut TestAppContext,
 8206) {
 8207    init_test(cx, |_| {});
 8208
 8209    cx.update(|cx| {
 8210        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8211            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8212                settings.auto_signature_help = Some(true);
 8213            });
 8214        });
 8215    });
 8216
 8217    let mut cx = EditorLspTestContext::new_rust(
 8218        lsp::ServerCapabilities {
 8219            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8220                ..Default::default()
 8221            }),
 8222            ..Default::default()
 8223        },
 8224        cx,
 8225    )
 8226    .await;
 8227
 8228    let language = Language::new(
 8229        LanguageConfig {
 8230            name: "Rust".into(),
 8231            brackets: BracketPairConfig {
 8232                pairs: vec![
 8233                    BracketPair {
 8234                        start: "{".to_string(),
 8235                        end: "}".to_string(),
 8236                        close: true,
 8237                        surround: true,
 8238                        newline: true,
 8239                    },
 8240                    BracketPair {
 8241                        start: "(".to_string(),
 8242                        end: ")".to_string(),
 8243                        close: true,
 8244                        surround: true,
 8245                        newline: true,
 8246                    },
 8247                    BracketPair {
 8248                        start: "/*".to_string(),
 8249                        end: " */".to_string(),
 8250                        close: true,
 8251                        surround: true,
 8252                        newline: true,
 8253                    },
 8254                    BracketPair {
 8255                        start: "[".to_string(),
 8256                        end: "]".to_string(),
 8257                        close: false,
 8258                        surround: false,
 8259                        newline: true,
 8260                    },
 8261                    BracketPair {
 8262                        start: "\"".to_string(),
 8263                        end: "\"".to_string(),
 8264                        close: true,
 8265                        surround: true,
 8266                        newline: false,
 8267                    },
 8268                    BracketPair {
 8269                        start: "<".to_string(),
 8270                        end: ">".to_string(),
 8271                        close: false,
 8272                        surround: true,
 8273                        newline: true,
 8274                    },
 8275                ],
 8276                ..Default::default()
 8277            },
 8278            autoclose_before: "})]".to_string(),
 8279            ..Default::default()
 8280        },
 8281        Some(tree_sitter_rust::LANGUAGE.into()),
 8282    );
 8283    let language = Arc::new(language);
 8284
 8285    cx.language_registry().add(language.clone());
 8286    cx.update_buffer(|buffer, cx| {
 8287        buffer.set_language(Some(language), cx);
 8288    });
 8289
 8290    cx.set_state(
 8291        &r#"
 8292            fn main() {
 8293                sampleˇ
 8294            }
 8295        "#
 8296        .unindent(),
 8297    );
 8298
 8299    cx.update_editor(|editor, window, cx| {
 8300        editor.handle_input("(", window, cx);
 8301    });
 8302    cx.assert_editor_state(
 8303        &"
 8304            fn main() {
 8305                sample(ˇ)
 8306            }
 8307        "
 8308        .unindent(),
 8309    );
 8310
 8311    let mocked_response = lsp::SignatureHelp {
 8312        signatures: vec![lsp::SignatureInformation {
 8313            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8314            documentation: None,
 8315            parameters: Some(vec![
 8316                lsp::ParameterInformation {
 8317                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8318                    documentation: None,
 8319                },
 8320                lsp::ParameterInformation {
 8321                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8322                    documentation: None,
 8323                },
 8324            ]),
 8325            active_parameter: None,
 8326        }],
 8327        active_signature: Some(0),
 8328        active_parameter: Some(0),
 8329    };
 8330    handle_signature_help_request(&mut cx, mocked_response).await;
 8331
 8332    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8333        .await;
 8334
 8335    cx.editor(|editor, _, _| {
 8336        let signature_help_state = editor.signature_help_state.popover().cloned();
 8337        assert_eq!(
 8338            signature_help_state.unwrap().label,
 8339            "param1: u8, param2: u8"
 8340        );
 8341    });
 8342}
 8343
 8344#[gpui::test]
 8345async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8346    init_test(cx, |_| {});
 8347
 8348    cx.update(|cx| {
 8349        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8350            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8351                settings.auto_signature_help = Some(false);
 8352                settings.show_signature_help_after_edits = Some(false);
 8353            });
 8354        });
 8355    });
 8356
 8357    let mut cx = EditorLspTestContext::new_rust(
 8358        lsp::ServerCapabilities {
 8359            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8360                ..Default::default()
 8361            }),
 8362            ..Default::default()
 8363        },
 8364        cx,
 8365    )
 8366    .await;
 8367
 8368    let language = Language::new(
 8369        LanguageConfig {
 8370            name: "Rust".into(),
 8371            brackets: BracketPairConfig {
 8372                pairs: vec![
 8373                    BracketPair {
 8374                        start: "{".to_string(),
 8375                        end: "}".to_string(),
 8376                        close: true,
 8377                        surround: true,
 8378                        newline: true,
 8379                    },
 8380                    BracketPair {
 8381                        start: "(".to_string(),
 8382                        end: ")".to_string(),
 8383                        close: true,
 8384                        surround: true,
 8385                        newline: true,
 8386                    },
 8387                    BracketPair {
 8388                        start: "/*".to_string(),
 8389                        end: " */".to_string(),
 8390                        close: true,
 8391                        surround: true,
 8392                        newline: true,
 8393                    },
 8394                    BracketPair {
 8395                        start: "[".to_string(),
 8396                        end: "]".to_string(),
 8397                        close: false,
 8398                        surround: false,
 8399                        newline: true,
 8400                    },
 8401                    BracketPair {
 8402                        start: "\"".to_string(),
 8403                        end: "\"".to_string(),
 8404                        close: true,
 8405                        surround: true,
 8406                        newline: false,
 8407                    },
 8408                    BracketPair {
 8409                        start: "<".to_string(),
 8410                        end: ">".to_string(),
 8411                        close: false,
 8412                        surround: true,
 8413                        newline: true,
 8414                    },
 8415                ],
 8416                ..Default::default()
 8417            },
 8418            autoclose_before: "})]".to_string(),
 8419            ..Default::default()
 8420        },
 8421        Some(tree_sitter_rust::LANGUAGE.into()),
 8422    );
 8423    let language = Arc::new(language);
 8424
 8425    cx.language_registry().add(language.clone());
 8426    cx.update_buffer(|buffer, cx| {
 8427        buffer.set_language(Some(language), cx);
 8428    });
 8429
 8430    // Ensure that signature_help is not called when no signature help is enabled.
 8431    cx.set_state(
 8432        &r#"
 8433            fn main() {
 8434                sampleˇ
 8435            }
 8436        "#
 8437        .unindent(),
 8438    );
 8439    cx.update_editor(|editor, window, cx| {
 8440        editor.handle_input("(", window, cx);
 8441    });
 8442    cx.assert_editor_state(
 8443        &"
 8444            fn main() {
 8445                sample(ˇ)
 8446            }
 8447        "
 8448        .unindent(),
 8449    );
 8450    cx.editor(|editor, _, _| {
 8451        assert!(editor.signature_help_state.task().is_none());
 8452    });
 8453
 8454    let mocked_response = lsp::SignatureHelp {
 8455        signatures: vec![lsp::SignatureInformation {
 8456            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8457            documentation: None,
 8458            parameters: Some(vec![
 8459                lsp::ParameterInformation {
 8460                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8461                    documentation: None,
 8462                },
 8463                lsp::ParameterInformation {
 8464                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8465                    documentation: None,
 8466                },
 8467            ]),
 8468            active_parameter: None,
 8469        }],
 8470        active_signature: Some(0),
 8471        active_parameter: Some(0),
 8472    };
 8473
 8474    // Ensure that signature_help is called when enabled afte edits
 8475    cx.update(|_, cx| {
 8476        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8477            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8478                settings.auto_signature_help = Some(false);
 8479                settings.show_signature_help_after_edits = Some(true);
 8480            });
 8481        });
 8482    });
 8483    cx.set_state(
 8484        &r#"
 8485            fn main() {
 8486                sampleˇ
 8487            }
 8488        "#
 8489        .unindent(),
 8490    );
 8491    cx.update_editor(|editor, window, cx| {
 8492        editor.handle_input("(", window, cx);
 8493    });
 8494    cx.assert_editor_state(
 8495        &"
 8496            fn main() {
 8497                sample(ˇ)
 8498            }
 8499        "
 8500        .unindent(),
 8501    );
 8502    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8503    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8504        .await;
 8505    cx.update_editor(|editor, _, _| {
 8506        let signature_help_state = editor.signature_help_state.popover().cloned();
 8507        assert!(signature_help_state.is_some());
 8508        assert_eq!(
 8509            signature_help_state.unwrap().label,
 8510            "param1: u8, param2: u8"
 8511        );
 8512        editor.signature_help_state = SignatureHelpState::default();
 8513    });
 8514
 8515    // Ensure that signature_help is called when auto signature help override is enabled
 8516    cx.update(|_, cx| {
 8517        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8518            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8519                settings.auto_signature_help = Some(true);
 8520                settings.show_signature_help_after_edits = Some(false);
 8521            });
 8522        });
 8523    });
 8524    cx.set_state(
 8525        &r#"
 8526            fn main() {
 8527                sampleˇ
 8528            }
 8529        "#
 8530        .unindent(),
 8531    );
 8532    cx.update_editor(|editor, window, cx| {
 8533        editor.handle_input("(", window, cx);
 8534    });
 8535    cx.assert_editor_state(
 8536        &"
 8537            fn main() {
 8538                sample(ˇ)
 8539            }
 8540        "
 8541        .unindent(),
 8542    );
 8543    handle_signature_help_request(&mut cx, mocked_response).await;
 8544    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8545        .await;
 8546    cx.editor(|editor, _, _| {
 8547        let signature_help_state = editor.signature_help_state.popover().cloned();
 8548        assert!(signature_help_state.is_some());
 8549        assert_eq!(
 8550            signature_help_state.unwrap().label,
 8551            "param1: u8, param2: u8"
 8552        );
 8553    });
 8554}
 8555
 8556#[gpui::test]
 8557async fn test_signature_help(cx: &mut TestAppContext) {
 8558    init_test(cx, |_| {});
 8559    cx.update(|cx| {
 8560        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8561            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8562                settings.auto_signature_help = Some(true);
 8563            });
 8564        });
 8565    });
 8566
 8567    let mut cx = EditorLspTestContext::new_rust(
 8568        lsp::ServerCapabilities {
 8569            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8570                ..Default::default()
 8571            }),
 8572            ..Default::default()
 8573        },
 8574        cx,
 8575    )
 8576    .await;
 8577
 8578    // A test that directly calls `show_signature_help`
 8579    cx.update_editor(|editor, window, cx| {
 8580        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8581    });
 8582
 8583    let mocked_response = lsp::SignatureHelp {
 8584        signatures: vec![lsp::SignatureInformation {
 8585            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8586            documentation: None,
 8587            parameters: Some(vec![
 8588                lsp::ParameterInformation {
 8589                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8590                    documentation: None,
 8591                },
 8592                lsp::ParameterInformation {
 8593                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8594                    documentation: None,
 8595                },
 8596            ]),
 8597            active_parameter: None,
 8598        }],
 8599        active_signature: Some(0),
 8600        active_parameter: Some(0),
 8601    };
 8602    handle_signature_help_request(&mut cx, mocked_response).await;
 8603
 8604    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8605        .await;
 8606
 8607    cx.editor(|editor, _, _| {
 8608        let signature_help_state = editor.signature_help_state.popover().cloned();
 8609        assert!(signature_help_state.is_some());
 8610        assert_eq!(
 8611            signature_help_state.unwrap().label,
 8612            "param1: u8, param2: u8"
 8613        );
 8614    });
 8615
 8616    // When exiting outside from inside the brackets, `signature_help` is closed.
 8617    cx.set_state(indoc! {"
 8618        fn main() {
 8619            sample(ˇ);
 8620        }
 8621
 8622        fn sample(param1: u8, param2: u8) {}
 8623    "});
 8624
 8625    cx.update_editor(|editor, window, cx| {
 8626        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8627    });
 8628
 8629    let mocked_response = lsp::SignatureHelp {
 8630        signatures: Vec::new(),
 8631        active_signature: None,
 8632        active_parameter: None,
 8633    };
 8634    handle_signature_help_request(&mut cx, mocked_response).await;
 8635
 8636    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8637        .await;
 8638
 8639    cx.editor(|editor, _, _| {
 8640        assert!(!editor.signature_help_state.is_shown());
 8641    });
 8642
 8643    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8644    cx.set_state(indoc! {"
 8645        fn main() {
 8646            sample(ˇ);
 8647        }
 8648
 8649        fn sample(param1: u8, param2: u8) {}
 8650    "});
 8651
 8652    let mocked_response = lsp::SignatureHelp {
 8653        signatures: vec![lsp::SignatureInformation {
 8654            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8655            documentation: None,
 8656            parameters: Some(vec![
 8657                lsp::ParameterInformation {
 8658                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8659                    documentation: None,
 8660                },
 8661                lsp::ParameterInformation {
 8662                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8663                    documentation: None,
 8664                },
 8665            ]),
 8666            active_parameter: None,
 8667        }],
 8668        active_signature: Some(0),
 8669        active_parameter: Some(0),
 8670    };
 8671    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8672    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8673        .await;
 8674    cx.editor(|editor, _, _| {
 8675        assert!(editor.signature_help_state.is_shown());
 8676    });
 8677
 8678    // Restore the popover with more parameter input
 8679    cx.set_state(indoc! {"
 8680        fn main() {
 8681            sample(param1, param2ˇ);
 8682        }
 8683
 8684        fn sample(param1: u8, param2: u8) {}
 8685    "});
 8686
 8687    let mocked_response = lsp::SignatureHelp {
 8688        signatures: vec![lsp::SignatureInformation {
 8689            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8690            documentation: None,
 8691            parameters: Some(vec![
 8692                lsp::ParameterInformation {
 8693                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8694                    documentation: None,
 8695                },
 8696                lsp::ParameterInformation {
 8697                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8698                    documentation: None,
 8699                },
 8700            ]),
 8701            active_parameter: None,
 8702        }],
 8703        active_signature: Some(0),
 8704        active_parameter: Some(1),
 8705    };
 8706    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8707    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8708        .await;
 8709
 8710    // When selecting a range, the popover is gone.
 8711    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8712    cx.update_editor(|editor, window, cx| {
 8713        editor.change_selections(None, window, cx, |s| {
 8714            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8715        })
 8716    });
 8717    cx.assert_editor_state(indoc! {"
 8718        fn main() {
 8719            sample(param1, «ˇparam2»);
 8720        }
 8721
 8722        fn sample(param1: u8, param2: u8) {}
 8723    "});
 8724    cx.editor(|editor, _, _| {
 8725        assert!(!editor.signature_help_state.is_shown());
 8726    });
 8727
 8728    // When unselecting again, the popover is back if within the brackets.
 8729    cx.update_editor(|editor, window, cx| {
 8730        editor.change_selections(None, window, cx, |s| {
 8731            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8732        })
 8733    });
 8734    cx.assert_editor_state(indoc! {"
 8735        fn main() {
 8736            sample(param1, ˇparam2);
 8737        }
 8738
 8739        fn sample(param1: u8, param2: u8) {}
 8740    "});
 8741    handle_signature_help_request(&mut cx, mocked_response).await;
 8742    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8743        .await;
 8744    cx.editor(|editor, _, _| {
 8745        assert!(editor.signature_help_state.is_shown());
 8746    });
 8747
 8748    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8749    cx.update_editor(|editor, window, cx| {
 8750        editor.change_selections(None, window, cx, |s| {
 8751            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8752            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8753        })
 8754    });
 8755    cx.assert_editor_state(indoc! {"
 8756        fn main() {
 8757            sample(param1, ˇparam2);
 8758        }
 8759
 8760        fn sample(param1: u8, param2: u8) {}
 8761    "});
 8762
 8763    let mocked_response = lsp::SignatureHelp {
 8764        signatures: vec![lsp::SignatureInformation {
 8765            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8766            documentation: None,
 8767            parameters: Some(vec![
 8768                lsp::ParameterInformation {
 8769                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8770                    documentation: None,
 8771                },
 8772                lsp::ParameterInformation {
 8773                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8774                    documentation: None,
 8775                },
 8776            ]),
 8777            active_parameter: None,
 8778        }],
 8779        active_signature: Some(0),
 8780        active_parameter: Some(1),
 8781    };
 8782    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8783    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8784        .await;
 8785    cx.update_editor(|editor, _, cx| {
 8786        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8787    });
 8788    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8789        .await;
 8790    cx.update_editor(|editor, window, cx| {
 8791        editor.change_selections(None, window, cx, |s| {
 8792            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8793        })
 8794    });
 8795    cx.assert_editor_state(indoc! {"
 8796        fn main() {
 8797            sample(param1, «ˇparam2»);
 8798        }
 8799
 8800        fn sample(param1: u8, param2: u8) {}
 8801    "});
 8802    cx.update_editor(|editor, window, cx| {
 8803        editor.change_selections(None, window, cx, |s| {
 8804            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8805        })
 8806    });
 8807    cx.assert_editor_state(indoc! {"
 8808        fn main() {
 8809            sample(param1, ˇparam2);
 8810        }
 8811
 8812        fn sample(param1: u8, param2: u8) {}
 8813    "});
 8814    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8815        .await;
 8816}
 8817
 8818#[gpui::test]
 8819async fn test_completion(cx: &mut TestAppContext) {
 8820    init_test(cx, |_| {});
 8821
 8822    let mut cx = EditorLspTestContext::new_rust(
 8823        lsp::ServerCapabilities {
 8824            completion_provider: Some(lsp::CompletionOptions {
 8825                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8826                resolve_provider: Some(true),
 8827                ..Default::default()
 8828            }),
 8829            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8830            ..Default::default()
 8831        },
 8832        cx,
 8833    )
 8834    .await;
 8835    let counter = Arc::new(AtomicUsize::new(0));
 8836
 8837    cx.set_state(indoc! {"
 8838        oneˇ
 8839        two
 8840        three
 8841    "});
 8842    cx.simulate_keystroke(".");
 8843    handle_completion_request(
 8844        &mut cx,
 8845        indoc! {"
 8846            one.|<>
 8847            two
 8848            three
 8849        "},
 8850        vec!["first_completion", "second_completion"],
 8851        counter.clone(),
 8852    )
 8853    .await;
 8854    cx.condition(|editor, _| editor.context_menu_visible())
 8855        .await;
 8856    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8857
 8858    let _handler = handle_signature_help_request(
 8859        &mut cx,
 8860        lsp::SignatureHelp {
 8861            signatures: vec![lsp::SignatureInformation {
 8862                label: "test signature".to_string(),
 8863                documentation: None,
 8864                parameters: Some(vec![lsp::ParameterInformation {
 8865                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8866                    documentation: None,
 8867                }]),
 8868                active_parameter: None,
 8869            }],
 8870            active_signature: None,
 8871            active_parameter: None,
 8872        },
 8873    );
 8874    cx.update_editor(|editor, window, cx| {
 8875        assert!(
 8876            !editor.signature_help_state.is_shown(),
 8877            "No signature help was called for"
 8878        );
 8879        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8880    });
 8881    cx.run_until_parked();
 8882    cx.update_editor(|editor, _, _| {
 8883        assert!(
 8884            !editor.signature_help_state.is_shown(),
 8885            "No signature help should be shown when completions menu is open"
 8886        );
 8887    });
 8888
 8889    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8890        editor.context_menu_next(&Default::default(), window, cx);
 8891        editor
 8892            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8893            .unwrap()
 8894    });
 8895    cx.assert_editor_state(indoc! {"
 8896        one.second_completionˇ
 8897        two
 8898        three
 8899    "});
 8900
 8901    handle_resolve_completion_request(
 8902        &mut cx,
 8903        Some(vec![
 8904            (
 8905                //This overlaps with the primary completion edit which is
 8906                //misbehavior from the LSP spec, test that we filter it out
 8907                indoc! {"
 8908                    one.second_ˇcompletion
 8909                    two
 8910                    threeˇ
 8911                "},
 8912                "overlapping additional edit",
 8913            ),
 8914            (
 8915                indoc! {"
 8916                    one.second_completion
 8917                    two
 8918                    threeˇ
 8919                "},
 8920                "\nadditional edit",
 8921            ),
 8922        ]),
 8923    )
 8924    .await;
 8925    apply_additional_edits.await.unwrap();
 8926    cx.assert_editor_state(indoc! {"
 8927        one.second_completionˇ
 8928        two
 8929        three
 8930        additional edit
 8931    "});
 8932
 8933    cx.set_state(indoc! {"
 8934        one.second_completion
 8935        twoˇ
 8936        threeˇ
 8937        additional edit
 8938    "});
 8939    cx.simulate_keystroke(" ");
 8940    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8941    cx.simulate_keystroke("s");
 8942    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8943
 8944    cx.assert_editor_state(indoc! {"
 8945        one.second_completion
 8946        two sˇ
 8947        three sˇ
 8948        additional edit
 8949    "});
 8950    handle_completion_request(
 8951        &mut cx,
 8952        indoc! {"
 8953            one.second_completion
 8954            two s
 8955            three <s|>
 8956            additional edit
 8957        "},
 8958        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8959        counter.clone(),
 8960    )
 8961    .await;
 8962    cx.condition(|editor, _| editor.context_menu_visible())
 8963        .await;
 8964    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8965
 8966    cx.simulate_keystroke("i");
 8967
 8968    handle_completion_request(
 8969        &mut cx,
 8970        indoc! {"
 8971            one.second_completion
 8972            two si
 8973            three <si|>
 8974            additional edit
 8975        "},
 8976        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8977        counter.clone(),
 8978    )
 8979    .await;
 8980    cx.condition(|editor, _| editor.context_menu_visible())
 8981        .await;
 8982    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8983
 8984    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8985        editor
 8986            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8987            .unwrap()
 8988    });
 8989    cx.assert_editor_state(indoc! {"
 8990        one.second_completion
 8991        two sixth_completionˇ
 8992        three sixth_completionˇ
 8993        additional edit
 8994    "});
 8995
 8996    apply_additional_edits.await.unwrap();
 8997
 8998    update_test_language_settings(&mut cx, |settings| {
 8999        settings.defaults.show_completions_on_input = Some(false);
 9000    });
 9001    cx.set_state("editorˇ");
 9002    cx.simulate_keystroke(".");
 9003    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9004    cx.simulate_keystroke("c");
 9005    cx.simulate_keystroke("l");
 9006    cx.simulate_keystroke("o");
 9007    cx.assert_editor_state("editor.cloˇ");
 9008    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9009    cx.update_editor(|editor, window, cx| {
 9010        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9011    });
 9012    handle_completion_request(
 9013        &mut cx,
 9014        "editor.<clo|>",
 9015        vec!["close", "clobber"],
 9016        counter.clone(),
 9017    )
 9018    .await;
 9019    cx.condition(|editor, _| editor.context_menu_visible())
 9020        .await;
 9021    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9022
 9023    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9024        editor
 9025            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9026            .unwrap()
 9027    });
 9028    cx.assert_editor_state("editor.closeˇ");
 9029    handle_resolve_completion_request(&mut cx, None).await;
 9030    apply_additional_edits.await.unwrap();
 9031}
 9032
 9033#[gpui::test]
 9034async fn test_multiline_completion(cx: &mut TestAppContext) {
 9035    init_test(cx, |_| {});
 9036
 9037    let fs = FakeFs::new(cx.executor());
 9038    fs.insert_tree(
 9039        path!("/a"),
 9040        json!({
 9041            "main.ts": "a",
 9042        }),
 9043    )
 9044    .await;
 9045
 9046    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9047    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9048    let typescript_language = Arc::new(Language::new(
 9049        LanguageConfig {
 9050            name: "TypeScript".into(),
 9051            matcher: LanguageMatcher {
 9052                path_suffixes: vec!["ts".to_string()],
 9053                ..LanguageMatcher::default()
 9054            },
 9055            line_comments: vec!["// ".into()],
 9056            ..LanguageConfig::default()
 9057        },
 9058        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9059    ));
 9060    language_registry.add(typescript_language.clone());
 9061    let mut fake_servers = language_registry.register_fake_lsp(
 9062        "TypeScript",
 9063        FakeLspAdapter {
 9064            capabilities: lsp::ServerCapabilities {
 9065                completion_provider: Some(lsp::CompletionOptions {
 9066                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9067                    ..lsp::CompletionOptions::default()
 9068                }),
 9069                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9070                ..lsp::ServerCapabilities::default()
 9071            },
 9072            // Emulate vtsls label generation
 9073            label_for_completion: Some(Box::new(|item, _| {
 9074                let text = if let Some(description) = item
 9075                    .label_details
 9076                    .as_ref()
 9077                    .and_then(|label_details| label_details.description.as_ref())
 9078                {
 9079                    format!("{} {}", item.label, description)
 9080                } else if let Some(detail) = &item.detail {
 9081                    format!("{} {}", item.label, detail)
 9082                } else {
 9083                    item.label.clone()
 9084                };
 9085                let len = text.len();
 9086                Some(language::CodeLabel {
 9087                    text,
 9088                    runs: Vec::new(),
 9089                    filter_range: 0..len,
 9090                })
 9091            })),
 9092            ..FakeLspAdapter::default()
 9093        },
 9094    );
 9095    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9096    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9097    let worktree_id = workspace
 9098        .update(cx, |workspace, _window, cx| {
 9099            workspace.project().update(cx, |project, cx| {
 9100                project.worktrees(cx).next().unwrap().read(cx).id()
 9101            })
 9102        })
 9103        .unwrap();
 9104    let _buffer = project
 9105        .update(cx, |project, cx| {
 9106            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9107        })
 9108        .await
 9109        .unwrap();
 9110    let editor = workspace
 9111        .update(cx, |workspace, window, cx| {
 9112            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9113        })
 9114        .unwrap()
 9115        .await
 9116        .unwrap()
 9117        .downcast::<Editor>()
 9118        .unwrap();
 9119    let fake_server = fake_servers.next().await.unwrap();
 9120
 9121    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9122    let multiline_label_2 = "a\nb\nc\n";
 9123    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9124    let multiline_description = "d\ne\nf\n";
 9125    let multiline_detail_2 = "g\nh\ni\n";
 9126
 9127    let mut completion_handle =
 9128        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9129            Ok(Some(lsp::CompletionResponse::Array(vec![
 9130                lsp::CompletionItem {
 9131                    label: multiline_label.to_string(),
 9132                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9133                        range: lsp::Range {
 9134                            start: lsp::Position {
 9135                                line: params.text_document_position.position.line,
 9136                                character: params.text_document_position.position.character,
 9137                            },
 9138                            end: lsp::Position {
 9139                                line: params.text_document_position.position.line,
 9140                                character: params.text_document_position.position.character,
 9141                            },
 9142                        },
 9143                        new_text: "new_text_1".to_string(),
 9144                    })),
 9145                    ..lsp::CompletionItem::default()
 9146                },
 9147                lsp::CompletionItem {
 9148                    label: "single line label 1".to_string(),
 9149                    detail: Some(multiline_detail.to_string()),
 9150                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9151                        range: lsp::Range {
 9152                            start: lsp::Position {
 9153                                line: params.text_document_position.position.line,
 9154                                character: params.text_document_position.position.character,
 9155                            },
 9156                            end: lsp::Position {
 9157                                line: params.text_document_position.position.line,
 9158                                character: params.text_document_position.position.character,
 9159                            },
 9160                        },
 9161                        new_text: "new_text_2".to_string(),
 9162                    })),
 9163                    ..lsp::CompletionItem::default()
 9164                },
 9165                lsp::CompletionItem {
 9166                    label: "single line label 2".to_string(),
 9167                    label_details: Some(lsp::CompletionItemLabelDetails {
 9168                        description: Some(multiline_description.to_string()),
 9169                        detail: None,
 9170                    }),
 9171                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9172                        range: lsp::Range {
 9173                            start: lsp::Position {
 9174                                line: params.text_document_position.position.line,
 9175                                character: params.text_document_position.position.character,
 9176                            },
 9177                            end: lsp::Position {
 9178                                line: params.text_document_position.position.line,
 9179                                character: params.text_document_position.position.character,
 9180                            },
 9181                        },
 9182                        new_text: "new_text_2".to_string(),
 9183                    })),
 9184                    ..lsp::CompletionItem::default()
 9185                },
 9186                lsp::CompletionItem {
 9187                    label: multiline_label_2.to_string(),
 9188                    detail: Some(multiline_detail_2.to_string()),
 9189                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9190                        range: lsp::Range {
 9191                            start: lsp::Position {
 9192                                line: params.text_document_position.position.line,
 9193                                character: params.text_document_position.position.character,
 9194                            },
 9195                            end: lsp::Position {
 9196                                line: params.text_document_position.position.line,
 9197                                character: params.text_document_position.position.character,
 9198                            },
 9199                        },
 9200                        new_text: "new_text_3".to_string(),
 9201                    })),
 9202                    ..lsp::CompletionItem::default()
 9203                },
 9204                lsp::CompletionItem {
 9205                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9206                    detail: Some(
 9207                        "Details with many     spaces and \t but without newlines".to_string(),
 9208                    ),
 9209                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9210                        range: lsp::Range {
 9211                            start: lsp::Position {
 9212                                line: params.text_document_position.position.line,
 9213                                character: params.text_document_position.position.character,
 9214                            },
 9215                            end: lsp::Position {
 9216                                line: params.text_document_position.position.line,
 9217                                character: params.text_document_position.position.character,
 9218                            },
 9219                        },
 9220                        new_text: "new_text_4".to_string(),
 9221                    })),
 9222                    ..lsp::CompletionItem::default()
 9223                },
 9224            ])))
 9225        });
 9226
 9227    editor.update_in(cx, |editor, window, cx| {
 9228        cx.focus_self(window);
 9229        editor.move_to_end(&MoveToEnd, window, cx);
 9230        editor.handle_input(".", window, cx);
 9231    });
 9232    cx.run_until_parked();
 9233    completion_handle.next().await.unwrap();
 9234
 9235    editor.update(cx, |editor, _| {
 9236        assert!(editor.context_menu_visible());
 9237        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9238        {
 9239            let completion_labels = menu
 9240                .completions
 9241                .borrow()
 9242                .iter()
 9243                .map(|c| c.label.text.clone())
 9244                .collect::<Vec<_>>();
 9245            assert_eq!(
 9246                completion_labels,
 9247                &[
 9248                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9249                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9250                    "single line label 2 d e f ",
 9251                    "a b c g h i ",
 9252                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9253                ],
 9254                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9255            );
 9256
 9257            for completion in menu
 9258                .completions
 9259                .borrow()
 9260                .iter() {
 9261                    assert_eq!(
 9262                        completion.label.filter_range,
 9263                        0..completion.label.text.len(),
 9264                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9265                    );
 9266                }
 9267
 9268        } else {
 9269            panic!("expected completion menu to be open");
 9270        }
 9271    });
 9272}
 9273
 9274#[gpui::test]
 9275async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9276    init_test(cx, |_| {});
 9277    let mut cx = EditorLspTestContext::new_rust(
 9278        lsp::ServerCapabilities {
 9279            completion_provider: Some(lsp::CompletionOptions {
 9280                trigger_characters: Some(vec![".".to_string()]),
 9281                ..Default::default()
 9282            }),
 9283            ..Default::default()
 9284        },
 9285        cx,
 9286    )
 9287    .await;
 9288    cx.lsp
 9289        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9290            Ok(Some(lsp::CompletionResponse::Array(vec![
 9291                lsp::CompletionItem {
 9292                    label: "first".into(),
 9293                    ..Default::default()
 9294                },
 9295                lsp::CompletionItem {
 9296                    label: "last".into(),
 9297                    ..Default::default()
 9298                },
 9299            ])))
 9300        });
 9301    cx.set_state("variableˇ");
 9302    cx.simulate_keystroke(".");
 9303    cx.executor().run_until_parked();
 9304
 9305    cx.update_editor(|editor, _, _| {
 9306        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9307        {
 9308            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9309        } else {
 9310            panic!("expected completion menu to be open");
 9311        }
 9312    });
 9313
 9314    cx.update_editor(|editor, window, cx| {
 9315        editor.move_page_down(&MovePageDown::default(), window, cx);
 9316        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9317        {
 9318            assert!(
 9319                menu.selected_item == 1,
 9320                "expected PageDown to select the last item from the context menu"
 9321            );
 9322        } else {
 9323            panic!("expected completion menu to stay open after PageDown");
 9324        }
 9325    });
 9326
 9327    cx.update_editor(|editor, window, cx| {
 9328        editor.move_page_up(&MovePageUp::default(), window, cx);
 9329        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9330        {
 9331            assert!(
 9332                menu.selected_item == 0,
 9333                "expected PageUp to select the first item from the context menu"
 9334            );
 9335        } else {
 9336            panic!("expected completion menu to stay open after PageUp");
 9337        }
 9338    });
 9339}
 9340
 9341#[gpui::test]
 9342async fn test_completion_sort(cx: &mut TestAppContext) {
 9343    init_test(cx, |_| {});
 9344    let mut cx = EditorLspTestContext::new_rust(
 9345        lsp::ServerCapabilities {
 9346            completion_provider: Some(lsp::CompletionOptions {
 9347                trigger_characters: Some(vec![".".to_string()]),
 9348                ..Default::default()
 9349            }),
 9350            ..Default::default()
 9351        },
 9352        cx,
 9353    )
 9354    .await;
 9355    cx.lsp
 9356        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9357            Ok(Some(lsp::CompletionResponse::Array(vec![
 9358                lsp::CompletionItem {
 9359                    label: "Range".into(),
 9360                    sort_text: Some("a".into()),
 9361                    ..Default::default()
 9362                },
 9363                lsp::CompletionItem {
 9364                    label: "r".into(),
 9365                    sort_text: Some("b".into()),
 9366                    ..Default::default()
 9367                },
 9368                lsp::CompletionItem {
 9369                    label: "ret".into(),
 9370                    sort_text: Some("c".into()),
 9371                    ..Default::default()
 9372                },
 9373                lsp::CompletionItem {
 9374                    label: "return".into(),
 9375                    sort_text: Some("d".into()),
 9376                    ..Default::default()
 9377                },
 9378                lsp::CompletionItem {
 9379                    label: "slice".into(),
 9380                    sort_text: Some("d".into()),
 9381                    ..Default::default()
 9382                },
 9383            ])))
 9384        });
 9385    cx.set_state("");
 9386    cx.executor().run_until_parked();
 9387    cx.update_editor(|editor, window, cx| {
 9388        editor.show_completions(
 9389            &ShowCompletions {
 9390                trigger: Some("r".into()),
 9391            },
 9392            window,
 9393            cx,
 9394        );
 9395    });
 9396    cx.executor().run_until_parked();
 9397
 9398    cx.update_editor(|editor, _, _| {
 9399        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9400        {
 9401            assert_eq!(
 9402                completion_menu_entries(&menu),
 9403                &["r", "ret", "Range", "return"]
 9404            );
 9405        } else {
 9406            panic!("expected completion menu to be open");
 9407        }
 9408    });
 9409}
 9410
 9411#[gpui::test]
 9412async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9413    init_test(cx, |_| {});
 9414
 9415    let mut cx = EditorLspTestContext::new_rust(
 9416        lsp::ServerCapabilities {
 9417            completion_provider: Some(lsp::CompletionOptions {
 9418                trigger_characters: Some(vec![".".to_string()]),
 9419                resolve_provider: Some(true),
 9420                ..Default::default()
 9421            }),
 9422            ..Default::default()
 9423        },
 9424        cx,
 9425    )
 9426    .await;
 9427
 9428    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9429    cx.simulate_keystroke(".");
 9430    let completion_item = lsp::CompletionItem {
 9431        label: "Some".into(),
 9432        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9433        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9434        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9435            kind: lsp::MarkupKind::Markdown,
 9436            value: "```rust\nSome(2)\n```".to_string(),
 9437        })),
 9438        deprecated: Some(false),
 9439        sort_text: Some("Some".to_string()),
 9440        filter_text: Some("Some".to_string()),
 9441        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9442        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9443            range: lsp::Range {
 9444                start: lsp::Position {
 9445                    line: 0,
 9446                    character: 22,
 9447                },
 9448                end: lsp::Position {
 9449                    line: 0,
 9450                    character: 22,
 9451                },
 9452            },
 9453            new_text: "Some(2)".to_string(),
 9454        })),
 9455        additional_text_edits: Some(vec![lsp::TextEdit {
 9456            range: lsp::Range {
 9457                start: lsp::Position {
 9458                    line: 0,
 9459                    character: 20,
 9460                },
 9461                end: lsp::Position {
 9462                    line: 0,
 9463                    character: 22,
 9464                },
 9465            },
 9466            new_text: "".to_string(),
 9467        }]),
 9468        ..Default::default()
 9469    };
 9470
 9471    let closure_completion_item = completion_item.clone();
 9472    let counter = Arc::new(AtomicUsize::new(0));
 9473    let counter_clone = counter.clone();
 9474    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9475        let task_completion_item = closure_completion_item.clone();
 9476        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9477        async move {
 9478            Ok(Some(lsp::CompletionResponse::Array(vec![
 9479                task_completion_item,
 9480            ])))
 9481        }
 9482    });
 9483
 9484    cx.condition(|editor, _| editor.context_menu_visible())
 9485        .await;
 9486    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9487    assert!(request.next().await.is_some());
 9488    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9489
 9490    cx.simulate_keystroke("S");
 9491    cx.simulate_keystroke("o");
 9492    cx.simulate_keystroke("m");
 9493    cx.condition(|editor, _| editor.context_menu_visible())
 9494        .await;
 9495    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9496    assert!(request.next().await.is_some());
 9497    assert!(request.next().await.is_some());
 9498    assert!(request.next().await.is_some());
 9499    request.close();
 9500    assert!(request.next().await.is_none());
 9501    assert_eq!(
 9502        counter.load(atomic::Ordering::Acquire),
 9503        4,
 9504        "With the completions menu open, only one LSP request should happen per input"
 9505    );
 9506}
 9507
 9508#[gpui::test]
 9509async fn test_toggle_comment(cx: &mut TestAppContext) {
 9510    init_test(cx, |_| {});
 9511    let mut cx = EditorTestContext::new(cx).await;
 9512    let language = Arc::new(Language::new(
 9513        LanguageConfig {
 9514            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9515            ..Default::default()
 9516        },
 9517        Some(tree_sitter_rust::LANGUAGE.into()),
 9518    ));
 9519    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9520
 9521    // If multiple selections intersect a line, the line is only toggled once.
 9522    cx.set_state(indoc! {"
 9523        fn a() {
 9524            «//b();
 9525            ˇ»// «c();
 9526            //ˇ»  d();
 9527        }
 9528    "});
 9529
 9530    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9531
 9532    cx.assert_editor_state(indoc! {"
 9533        fn a() {
 9534            «b();
 9535            c();
 9536            ˇ» d();
 9537        }
 9538    "});
 9539
 9540    // The comment prefix is inserted at the same column for every line in a
 9541    // selection.
 9542    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9543
 9544    cx.assert_editor_state(indoc! {"
 9545        fn a() {
 9546            // «b();
 9547            // c();
 9548            ˇ»//  d();
 9549        }
 9550    "});
 9551
 9552    // If a selection ends at the beginning of a line, that line is not toggled.
 9553    cx.set_selections_state(indoc! {"
 9554        fn a() {
 9555            // b();
 9556            «// c();
 9557        ˇ»    //  d();
 9558        }
 9559    "});
 9560
 9561    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9562
 9563    cx.assert_editor_state(indoc! {"
 9564        fn a() {
 9565            // b();
 9566            «c();
 9567        ˇ»    //  d();
 9568        }
 9569    "});
 9570
 9571    // If a selection span a single line and is empty, the line is toggled.
 9572    cx.set_state(indoc! {"
 9573        fn a() {
 9574            a();
 9575            b();
 9576        ˇ
 9577        }
 9578    "});
 9579
 9580    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9581
 9582    cx.assert_editor_state(indoc! {"
 9583        fn a() {
 9584            a();
 9585            b();
 9586        //•ˇ
 9587        }
 9588    "});
 9589
 9590    // If a selection span multiple lines, empty lines are not toggled.
 9591    cx.set_state(indoc! {"
 9592        fn a() {
 9593            «a();
 9594
 9595            c();ˇ»
 9596        }
 9597    "});
 9598
 9599    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9600
 9601    cx.assert_editor_state(indoc! {"
 9602        fn a() {
 9603            // «a();
 9604
 9605            // c();ˇ»
 9606        }
 9607    "});
 9608
 9609    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9610    cx.set_state(indoc! {"
 9611        fn a() {
 9612            «// a();
 9613            /// b();
 9614            //! c();ˇ»
 9615        }
 9616    "});
 9617
 9618    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9619
 9620    cx.assert_editor_state(indoc! {"
 9621        fn a() {
 9622            «a();
 9623            b();
 9624            c();ˇ»
 9625        }
 9626    "});
 9627}
 9628
 9629#[gpui::test]
 9630async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9631    init_test(cx, |_| {});
 9632    let mut cx = EditorTestContext::new(cx).await;
 9633    let language = Arc::new(Language::new(
 9634        LanguageConfig {
 9635            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9636            ..Default::default()
 9637        },
 9638        Some(tree_sitter_rust::LANGUAGE.into()),
 9639    ));
 9640    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9641
 9642    let toggle_comments = &ToggleComments {
 9643        advance_downwards: false,
 9644        ignore_indent: true,
 9645    };
 9646
 9647    // If multiple selections intersect a line, the line is only toggled once.
 9648    cx.set_state(indoc! {"
 9649        fn a() {
 9650        //    «b();
 9651        //    c();
 9652        //    ˇ» d();
 9653        }
 9654    "});
 9655
 9656    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9657
 9658    cx.assert_editor_state(indoc! {"
 9659        fn a() {
 9660            «b();
 9661            c();
 9662            ˇ» d();
 9663        }
 9664    "});
 9665
 9666    // The comment prefix is inserted at the beginning of each line
 9667    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9668
 9669    cx.assert_editor_state(indoc! {"
 9670        fn a() {
 9671        //    «b();
 9672        //    c();
 9673        //    ˇ» d();
 9674        }
 9675    "});
 9676
 9677    // If a selection ends at the beginning of a line, that line is not toggled.
 9678    cx.set_selections_state(indoc! {"
 9679        fn a() {
 9680        //    b();
 9681        //    «c();
 9682        ˇ»//     d();
 9683        }
 9684    "});
 9685
 9686    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9687
 9688    cx.assert_editor_state(indoc! {"
 9689        fn a() {
 9690        //    b();
 9691            «c();
 9692        ˇ»//     d();
 9693        }
 9694    "});
 9695
 9696    // If a selection span a single line and is empty, the line is toggled.
 9697    cx.set_state(indoc! {"
 9698        fn a() {
 9699            a();
 9700            b();
 9701        ˇ
 9702        }
 9703    "});
 9704
 9705    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9706
 9707    cx.assert_editor_state(indoc! {"
 9708        fn a() {
 9709            a();
 9710            b();
 9711        //ˇ
 9712        }
 9713    "});
 9714
 9715    // If a selection span multiple lines, empty lines are not toggled.
 9716    cx.set_state(indoc! {"
 9717        fn a() {
 9718            «a();
 9719
 9720            c();ˇ»
 9721        }
 9722    "});
 9723
 9724    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9725
 9726    cx.assert_editor_state(indoc! {"
 9727        fn a() {
 9728        //    «a();
 9729
 9730        //    c();ˇ»
 9731        }
 9732    "});
 9733
 9734    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9735    cx.set_state(indoc! {"
 9736        fn a() {
 9737        //    «a();
 9738        ///    b();
 9739        //!    c();ˇ»
 9740        }
 9741    "});
 9742
 9743    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9744
 9745    cx.assert_editor_state(indoc! {"
 9746        fn a() {
 9747            «a();
 9748            b();
 9749            c();ˇ»
 9750        }
 9751    "});
 9752}
 9753
 9754#[gpui::test]
 9755async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9756    init_test(cx, |_| {});
 9757
 9758    let language = Arc::new(Language::new(
 9759        LanguageConfig {
 9760            line_comments: vec!["// ".into()],
 9761            ..Default::default()
 9762        },
 9763        Some(tree_sitter_rust::LANGUAGE.into()),
 9764    ));
 9765
 9766    let mut cx = EditorTestContext::new(cx).await;
 9767
 9768    cx.language_registry().add(language.clone());
 9769    cx.update_buffer(|buffer, cx| {
 9770        buffer.set_language(Some(language), cx);
 9771    });
 9772
 9773    let toggle_comments = &ToggleComments {
 9774        advance_downwards: true,
 9775        ignore_indent: false,
 9776    };
 9777
 9778    // Single cursor on one line -> advance
 9779    // Cursor moves horizontally 3 characters as well on non-blank line
 9780    cx.set_state(indoc!(
 9781        "fn a() {
 9782             ˇdog();
 9783             cat();
 9784        }"
 9785    ));
 9786    cx.update_editor(|editor, window, cx| {
 9787        editor.toggle_comments(toggle_comments, window, cx);
 9788    });
 9789    cx.assert_editor_state(indoc!(
 9790        "fn a() {
 9791             // dog();
 9792             catˇ();
 9793        }"
 9794    ));
 9795
 9796    // Single selection on one line -> don't advance
 9797    cx.set_state(indoc!(
 9798        "fn a() {
 9799             «dog()ˇ»;
 9800             cat();
 9801        }"
 9802    ));
 9803    cx.update_editor(|editor, window, cx| {
 9804        editor.toggle_comments(toggle_comments, window, cx);
 9805    });
 9806    cx.assert_editor_state(indoc!(
 9807        "fn a() {
 9808             // «dog()ˇ»;
 9809             cat();
 9810        }"
 9811    ));
 9812
 9813    // Multiple cursors on one line -> advance
 9814    cx.set_state(indoc!(
 9815        "fn a() {
 9816             ˇdˇog();
 9817             cat();
 9818        }"
 9819    ));
 9820    cx.update_editor(|editor, window, cx| {
 9821        editor.toggle_comments(toggle_comments, window, cx);
 9822    });
 9823    cx.assert_editor_state(indoc!(
 9824        "fn a() {
 9825             // dog();
 9826             catˇ(ˇ);
 9827        }"
 9828    ));
 9829
 9830    // Multiple cursors on one line, with selection -> don't advance
 9831    cx.set_state(indoc!(
 9832        "fn a() {
 9833             ˇdˇog«()ˇ»;
 9834             cat();
 9835        }"
 9836    ));
 9837    cx.update_editor(|editor, window, cx| {
 9838        editor.toggle_comments(toggle_comments, window, cx);
 9839    });
 9840    cx.assert_editor_state(indoc!(
 9841        "fn a() {
 9842             // ˇdˇog«()ˇ»;
 9843             cat();
 9844        }"
 9845    ));
 9846
 9847    // Single cursor on one line -> advance
 9848    // Cursor moves to column 0 on blank line
 9849    cx.set_state(indoc!(
 9850        "fn a() {
 9851             ˇdog();
 9852
 9853             cat();
 9854        }"
 9855    ));
 9856    cx.update_editor(|editor, window, cx| {
 9857        editor.toggle_comments(toggle_comments, window, cx);
 9858    });
 9859    cx.assert_editor_state(indoc!(
 9860        "fn a() {
 9861             // dog();
 9862        ˇ
 9863             cat();
 9864        }"
 9865    ));
 9866
 9867    // Single cursor on one line -> advance
 9868    // Cursor starts and ends at column 0
 9869    cx.set_state(indoc!(
 9870        "fn a() {
 9871         ˇ    dog();
 9872             cat();
 9873        }"
 9874    ));
 9875    cx.update_editor(|editor, window, cx| {
 9876        editor.toggle_comments(toggle_comments, window, cx);
 9877    });
 9878    cx.assert_editor_state(indoc!(
 9879        "fn a() {
 9880             // dog();
 9881         ˇ    cat();
 9882        }"
 9883    ));
 9884}
 9885
 9886#[gpui::test]
 9887async fn test_toggle_block_comment(cx: &mut TestAppContext) {
 9888    init_test(cx, |_| {});
 9889
 9890    let mut cx = EditorTestContext::new(cx).await;
 9891
 9892    let html_language = Arc::new(
 9893        Language::new(
 9894            LanguageConfig {
 9895                name: "HTML".into(),
 9896                block_comment: Some(("<!-- ".into(), " -->".into())),
 9897                ..Default::default()
 9898            },
 9899            Some(tree_sitter_html::LANGUAGE.into()),
 9900        )
 9901        .with_injection_query(
 9902            r#"
 9903            (script_element
 9904                (raw_text) @injection.content
 9905                (#set! injection.language "javascript"))
 9906            "#,
 9907        )
 9908        .unwrap(),
 9909    );
 9910
 9911    let javascript_language = Arc::new(Language::new(
 9912        LanguageConfig {
 9913            name: "JavaScript".into(),
 9914            line_comments: vec!["// ".into()],
 9915            ..Default::default()
 9916        },
 9917        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9918    ));
 9919
 9920    cx.language_registry().add(html_language.clone());
 9921    cx.language_registry().add(javascript_language.clone());
 9922    cx.update_buffer(|buffer, cx| {
 9923        buffer.set_language(Some(html_language), cx);
 9924    });
 9925
 9926    // Toggle comments for empty selections
 9927    cx.set_state(
 9928        &r#"
 9929            <p>A</p>ˇ
 9930            <p>B</p>ˇ
 9931            <p>C</p>ˇ
 9932        "#
 9933        .unindent(),
 9934    );
 9935    cx.update_editor(|editor, window, cx| {
 9936        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9937    });
 9938    cx.assert_editor_state(
 9939        &r#"
 9940            <!-- <p>A</p>ˇ -->
 9941            <!-- <p>B</p>ˇ -->
 9942            <!-- <p>C</p>ˇ -->
 9943        "#
 9944        .unindent(),
 9945    );
 9946    cx.update_editor(|editor, window, cx| {
 9947        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9948    });
 9949    cx.assert_editor_state(
 9950        &r#"
 9951            <p>A</p>ˇ
 9952            <p>B</p>ˇ
 9953            <p>C</p>ˇ
 9954        "#
 9955        .unindent(),
 9956    );
 9957
 9958    // Toggle comments for mixture of empty and non-empty selections, where
 9959    // multiple selections occupy a given line.
 9960    cx.set_state(
 9961        &r#"
 9962            <p>A«</p>
 9963            <p>ˇ»B</p>ˇ
 9964            <p>C«</p>
 9965            <p>ˇ»D</p>ˇ
 9966        "#
 9967        .unindent(),
 9968    );
 9969
 9970    cx.update_editor(|editor, window, cx| {
 9971        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9972    });
 9973    cx.assert_editor_state(
 9974        &r#"
 9975            <!-- <p>A«</p>
 9976            <p>ˇ»B</p>ˇ -->
 9977            <!-- <p>C«</p>
 9978            <p>ˇ»D</p>ˇ -->
 9979        "#
 9980        .unindent(),
 9981    );
 9982    cx.update_editor(|editor, window, cx| {
 9983        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9984    });
 9985    cx.assert_editor_state(
 9986        &r#"
 9987            <p>A«</p>
 9988            <p>ˇ»B</p>ˇ
 9989            <p>C«</p>
 9990            <p>ˇ»D</p>ˇ
 9991        "#
 9992        .unindent(),
 9993    );
 9994
 9995    // Toggle comments when different languages are active for different
 9996    // selections.
 9997    cx.set_state(
 9998        &r#"
 9999            ˇ<script>
10000                ˇvar x = new Y();
10001            ˇ</script>
10002        "#
10003        .unindent(),
10004    );
10005    cx.executor().run_until_parked();
10006    cx.update_editor(|editor, window, cx| {
10007        editor.toggle_comments(&ToggleComments::default(), window, cx)
10008    });
10009    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10010    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10011    cx.assert_editor_state(
10012        &r#"
10013            <!-- ˇ<script> -->
10014                // ˇvar x = new Y();
10015            <!-- ˇ</script> -->
10016        "#
10017        .unindent(),
10018    );
10019}
10020
10021#[gpui::test]
10022fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10023    init_test(cx, |_| {});
10024
10025    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10026    let multibuffer = cx.new(|cx| {
10027        let mut multibuffer = MultiBuffer::new(ReadWrite);
10028        multibuffer.push_excerpts(
10029            buffer.clone(),
10030            [
10031                ExcerptRange {
10032                    context: Point::new(0, 0)..Point::new(0, 4),
10033                    primary: None,
10034                },
10035                ExcerptRange {
10036                    context: Point::new(1, 0)..Point::new(1, 4),
10037                    primary: None,
10038                },
10039            ],
10040            cx,
10041        );
10042        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10043        multibuffer
10044    });
10045
10046    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10047    editor.update_in(cx, |editor, window, cx| {
10048        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10049        editor.change_selections(None, window, cx, |s| {
10050            s.select_ranges([
10051                Point::new(0, 0)..Point::new(0, 0),
10052                Point::new(1, 0)..Point::new(1, 0),
10053            ])
10054        });
10055
10056        editor.handle_input("X", window, cx);
10057        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10058        assert_eq!(
10059            editor.selections.ranges(cx),
10060            [
10061                Point::new(0, 1)..Point::new(0, 1),
10062                Point::new(1, 1)..Point::new(1, 1),
10063            ]
10064        );
10065
10066        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10067        editor.change_selections(None, window, cx, |s| {
10068            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10069        });
10070        editor.backspace(&Default::default(), window, cx);
10071        assert_eq!(editor.text(cx), "Xa\nbbb");
10072        assert_eq!(
10073            editor.selections.ranges(cx),
10074            [Point::new(1, 0)..Point::new(1, 0)]
10075        );
10076
10077        editor.change_selections(None, window, cx, |s| {
10078            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10079        });
10080        editor.backspace(&Default::default(), window, cx);
10081        assert_eq!(editor.text(cx), "X\nbb");
10082        assert_eq!(
10083            editor.selections.ranges(cx),
10084            [Point::new(0, 1)..Point::new(0, 1)]
10085        );
10086    });
10087}
10088
10089#[gpui::test]
10090fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10091    init_test(cx, |_| {});
10092
10093    let markers = vec![('[', ']').into(), ('(', ')').into()];
10094    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10095        indoc! {"
10096            [aaaa
10097            (bbbb]
10098            cccc)",
10099        },
10100        markers.clone(),
10101    );
10102    let excerpt_ranges = markers.into_iter().map(|marker| {
10103        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10104        ExcerptRange {
10105            context,
10106            primary: None,
10107        }
10108    });
10109    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10110    let multibuffer = cx.new(|cx| {
10111        let mut multibuffer = MultiBuffer::new(ReadWrite);
10112        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10113        multibuffer
10114    });
10115
10116    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10117    editor.update_in(cx, |editor, window, cx| {
10118        let (expected_text, selection_ranges) = marked_text_ranges(
10119            indoc! {"
10120                aaaa
10121                bˇbbb
10122                bˇbbˇb
10123                cccc"
10124            },
10125            true,
10126        );
10127        assert_eq!(editor.text(cx), expected_text);
10128        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10129
10130        editor.handle_input("X", window, cx);
10131
10132        let (expected_text, expected_selections) = marked_text_ranges(
10133            indoc! {"
10134                aaaa
10135                bXˇbbXb
10136                bXˇbbXˇb
10137                cccc"
10138            },
10139            false,
10140        );
10141        assert_eq!(editor.text(cx), expected_text);
10142        assert_eq!(editor.selections.ranges(cx), expected_selections);
10143
10144        editor.newline(&Newline, window, cx);
10145        let (expected_text, expected_selections) = marked_text_ranges(
10146            indoc! {"
10147                aaaa
10148                bX
10149                ˇbbX
10150                b
10151                bX
10152                ˇbbX
10153                ˇb
10154                cccc"
10155            },
10156            false,
10157        );
10158        assert_eq!(editor.text(cx), expected_text);
10159        assert_eq!(editor.selections.ranges(cx), expected_selections);
10160    });
10161}
10162
10163#[gpui::test]
10164fn test_refresh_selections(cx: &mut TestAppContext) {
10165    init_test(cx, |_| {});
10166
10167    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10168    let mut excerpt1_id = None;
10169    let multibuffer = cx.new(|cx| {
10170        let mut multibuffer = MultiBuffer::new(ReadWrite);
10171        excerpt1_id = multibuffer
10172            .push_excerpts(
10173                buffer.clone(),
10174                [
10175                    ExcerptRange {
10176                        context: Point::new(0, 0)..Point::new(1, 4),
10177                        primary: None,
10178                    },
10179                    ExcerptRange {
10180                        context: Point::new(1, 0)..Point::new(2, 4),
10181                        primary: None,
10182                    },
10183                ],
10184                cx,
10185            )
10186            .into_iter()
10187            .next();
10188        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10189        multibuffer
10190    });
10191
10192    let editor = cx.add_window(|window, cx| {
10193        let mut editor = build_editor(multibuffer.clone(), window, cx);
10194        let snapshot = editor.snapshot(window, cx);
10195        editor.change_selections(None, window, cx, |s| {
10196            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10197        });
10198        editor.begin_selection(
10199            Point::new(2, 1).to_display_point(&snapshot),
10200            true,
10201            1,
10202            window,
10203            cx,
10204        );
10205        assert_eq!(
10206            editor.selections.ranges(cx),
10207            [
10208                Point::new(1, 3)..Point::new(1, 3),
10209                Point::new(2, 1)..Point::new(2, 1),
10210            ]
10211        );
10212        editor
10213    });
10214
10215    // Refreshing selections is a no-op when excerpts haven't changed.
10216    _ = editor.update(cx, |editor, window, cx| {
10217        editor.change_selections(None, window, cx, |s| s.refresh());
10218        assert_eq!(
10219            editor.selections.ranges(cx),
10220            [
10221                Point::new(1, 3)..Point::new(1, 3),
10222                Point::new(2, 1)..Point::new(2, 1),
10223            ]
10224        );
10225    });
10226
10227    multibuffer.update(cx, |multibuffer, cx| {
10228        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10229    });
10230    _ = editor.update(cx, |editor, window, cx| {
10231        // Removing an excerpt causes the first selection to become degenerate.
10232        assert_eq!(
10233            editor.selections.ranges(cx),
10234            [
10235                Point::new(0, 0)..Point::new(0, 0),
10236                Point::new(0, 1)..Point::new(0, 1)
10237            ]
10238        );
10239
10240        // Refreshing selections will relocate the first selection to the original buffer
10241        // location.
10242        editor.change_selections(None, window, cx, |s| s.refresh());
10243        assert_eq!(
10244            editor.selections.ranges(cx),
10245            [
10246                Point::new(0, 1)..Point::new(0, 1),
10247                Point::new(0, 3)..Point::new(0, 3)
10248            ]
10249        );
10250        assert!(editor.selections.pending_anchor().is_some());
10251    });
10252}
10253
10254#[gpui::test]
10255fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10256    init_test(cx, |_| {});
10257
10258    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10259    let mut excerpt1_id = None;
10260    let multibuffer = cx.new(|cx| {
10261        let mut multibuffer = MultiBuffer::new(ReadWrite);
10262        excerpt1_id = multibuffer
10263            .push_excerpts(
10264                buffer.clone(),
10265                [
10266                    ExcerptRange {
10267                        context: Point::new(0, 0)..Point::new(1, 4),
10268                        primary: None,
10269                    },
10270                    ExcerptRange {
10271                        context: Point::new(1, 0)..Point::new(2, 4),
10272                        primary: None,
10273                    },
10274                ],
10275                cx,
10276            )
10277            .into_iter()
10278            .next();
10279        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10280        multibuffer
10281    });
10282
10283    let editor = cx.add_window(|window, cx| {
10284        let mut editor = build_editor(multibuffer.clone(), window, cx);
10285        let snapshot = editor.snapshot(window, cx);
10286        editor.begin_selection(
10287            Point::new(1, 3).to_display_point(&snapshot),
10288            false,
10289            1,
10290            window,
10291            cx,
10292        );
10293        assert_eq!(
10294            editor.selections.ranges(cx),
10295            [Point::new(1, 3)..Point::new(1, 3)]
10296        );
10297        editor
10298    });
10299
10300    multibuffer.update(cx, |multibuffer, cx| {
10301        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10302    });
10303    _ = editor.update(cx, |editor, window, cx| {
10304        assert_eq!(
10305            editor.selections.ranges(cx),
10306            [Point::new(0, 0)..Point::new(0, 0)]
10307        );
10308
10309        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10310        editor.change_selections(None, window, cx, |s| s.refresh());
10311        assert_eq!(
10312            editor.selections.ranges(cx),
10313            [Point::new(0, 3)..Point::new(0, 3)]
10314        );
10315        assert!(editor.selections.pending_anchor().is_some());
10316    });
10317}
10318
10319#[gpui::test]
10320async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10321    init_test(cx, |_| {});
10322
10323    let language = Arc::new(
10324        Language::new(
10325            LanguageConfig {
10326                brackets: BracketPairConfig {
10327                    pairs: vec![
10328                        BracketPair {
10329                            start: "{".to_string(),
10330                            end: "}".to_string(),
10331                            close: true,
10332                            surround: true,
10333                            newline: true,
10334                        },
10335                        BracketPair {
10336                            start: "/* ".to_string(),
10337                            end: " */".to_string(),
10338                            close: true,
10339                            surround: true,
10340                            newline: true,
10341                        },
10342                    ],
10343                    ..Default::default()
10344                },
10345                ..Default::default()
10346            },
10347            Some(tree_sitter_rust::LANGUAGE.into()),
10348        )
10349        .with_indents_query("")
10350        .unwrap(),
10351    );
10352
10353    let text = concat!(
10354        "{   }\n",     //
10355        "  x\n",       //
10356        "  /*   */\n", //
10357        "x\n",         //
10358        "{{} }\n",     //
10359    );
10360
10361    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10362    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10363    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10364    editor
10365        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10366        .await;
10367
10368    editor.update_in(cx, |editor, window, cx| {
10369        editor.change_selections(None, window, cx, |s| {
10370            s.select_display_ranges([
10371                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10372                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10373                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10374            ])
10375        });
10376        editor.newline(&Newline, window, cx);
10377
10378        assert_eq!(
10379            editor.buffer().read(cx).read(cx).text(),
10380            concat!(
10381                "{ \n",    // Suppress rustfmt
10382                "\n",      //
10383                "}\n",     //
10384                "  x\n",   //
10385                "  /* \n", //
10386                "  \n",    //
10387                "  */\n",  //
10388                "x\n",     //
10389                "{{} \n",  //
10390                "}\n",     //
10391            )
10392        );
10393    });
10394}
10395
10396#[gpui::test]
10397fn test_highlighted_ranges(cx: &mut TestAppContext) {
10398    init_test(cx, |_| {});
10399
10400    let editor = cx.add_window(|window, cx| {
10401        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10402        build_editor(buffer.clone(), window, cx)
10403    });
10404
10405    _ = editor.update(cx, |editor, window, cx| {
10406        struct Type1;
10407        struct Type2;
10408
10409        let buffer = editor.buffer.read(cx).snapshot(cx);
10410
10411        let anchor_range =
10412            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10413
10414        editor.highlight_background::<Type1>(
10415            &[
10416                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10417                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10418                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10419                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10420            ],
10421            |_| Hsla::red(),
10422            cx,
10423        );
10424        editor.highlight_background::<Type2>(
10425            &[
10426                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10427                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10428                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10429                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10430            ],
10431            |_| Hsla::green(),
10432            cx,
10433        );
10434
10435        let snapshot = editor.snapshot(window, cx);
10436        let mut highlighted_ranges = editor.background_highlights_in_range(
10437            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10438            &snapshot,
10439            cx.theme().colors(),
10440        );
10441        // Enforce a consistent ordering based on color without relying on the ordering of the
10442        // highlight's `TypeId` which is non-executor.
10443        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10444        assert_eq!(
10445            highlighted_ranges,
10446            &[
10447                (
10448                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10449                    Hsla::red(),
10450                ),
10451                (
10452                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10453                    Hsla::red(),
10454                ),
10455                (
10456                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10457                    Hsla::green(),
10458                ),
10459                (
10460                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10461                    Hsla::green(),
10462                ),
10463            ]
10464        );
10465        assert_eq!(
10466            editor.background_highlights_in_range(
10467                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10468                &snapshot,
10469                cx.theme().colors(),
10470            ),
10471            &[(
10472                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10473                Hsla::red(),
10474            )]
10475        );
10476    });
10477}
10478
10479#[gpui::test]
10480async fn test_following(cx: &mut TestAppContext) {
10481    init_test(cx, |_| {});
10482
10483    let fs = FakeFs::new(cx.executor());
10484    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10485
10486    let buffer = project.update(cx, |project, cx| {
10487        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10488        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10489    });
10490    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10491    let follower = cx.update(|cx| {
10492        cx.open_window(
10493            WindowOptions {
10494                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10495                    gpui::Point::new(px(0.), px(0.)),
10496                    gpui::Point::new(px(10.), px(80.)),
10497                ))),
10498                ..Default::default()
10499            },
10500            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10501        )
10502        .unwrap()
10503    });
10504
10505    let is_still_following = Rc::new(RefCell::new(true));
10506    let follower_edit_event_count = Rc::new(RefCell::new(0));
10507    let pending_update = Rc::new(RefCell::new(None));
10508    let leader_entity = leader.root(cx).unwrap();
10509    let follower_entity = follower.root(cx).unwrap();
10510    _ = follower.update(cx, {
10511        let update = pending_update.clone();
10512        let is_still_following = is_still_following.clone();
10513        let follower_edit_event_count = follower_edit_event_count.clone();
10514        |_, window, cx| {
10515            cx.subscribe_in(
10516                &leader_entity,
10517                window,
10518                move |_, leader, event, window, cx| {
10519                    leader.read(cx).add_event_to_update_proto(
10520                        event,
10521                        &mut update.borrow_mut(),
10522                        window,
10523                        cx,
10524                    );
10525                },
10526            )
10527            .detach();
10528
10529            cx.subscribe_in(
10530                &follower_entity,
10531                window,
10532                move |_, _, event: &EditorEvent, _window, _cx| {
10533                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10534                        *is_still_following.borrow_mut() = false;
10535                    }
10536
10537                    if let EditorEvent::BufferEdited = event {
10538                        *follower_edit_event_count.borrow_mut() += 1;
10539                    }
10540                },
10541            )
10542            .detach();
10543        }
10544    });
10545
10546    // Update the selections only
10547    _ = leader.update(cx, |leader, window, cx| {
10548        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10549    });
10550    follower
10551        .update(cx, |follower, window, cx| {
10552            follower.apply_update_proto(
10553                &project,
10554                pending_update.borrow_mut().take().unwrap(),
10555                window,
10556                cx,
10557            )
10558        })
10559        .unwrap()
10560        .await
10561        .unwrap();
10562    _ = follower.update(cx, |follower, _, cx| {
10563        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10564    });
10565    assert!(*is_still_following.borrow());
10566    assert_eq!(*follower_edit_event_count.borrow(), 0);
10567
10568    // Update the scroll position only
10569    _ = leader.update(cx, |leader, window, cx| {
10570        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10571    });
10572    follower
10573        .update(cx, |follower, window, cx| {
10574            follower.apply_update_proto(
10575                &project,
10576                pending_update.borrow_mut().take().unwrap(),
10577                window,
10578                cx,
10579            )
10580        })
10581        .unwrap()
10582        .await
10583        .unwrap();
10584    assert_eq!(
10585        follower
10586            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10587            .unwrap(),
10588        gpui::Point::new(1.5, 3.5)
10589    );
10590    assert!(*is_still_following.borrow());
10591    assert_eq!(*follower_edit_event_count.borrow(), 0);
10592
10593    // Update the selections and scroll position. The follower's scroll position is updated
10594    // via autoscroll, not via the leader's exact scroll position.
10595    _ = leader.update(cx, |leader, window, cx| {
10596        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10597        leader.request_autoscroll(Autoscroll::newest(), cx);
10598        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10599    });
10600    follower
10601        .update(cx, |follower, window, cx| {
10602            follower.apply_update_proto(
10603                &project,
10604                pending_update.borrow_mut().take().unwrap(),
10605                window,
10606                cx,
10607            )
10608        })
10609        .unwrap()
10610        .await
10611        .unwrap();
10612    _ = follower.update(cx, |follower, _, cx| {
10613        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10614        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10615    });
10616    assert!(*is_still_following.borrow());
10617
10618    // Creating a pending selection that precedes another selection
10619    _ = leader.update(cx, |leader, window, cx| {
10620        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10621        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10622    });
10623    follower
10624        .update(cx, |follower, window, cx| {
10625            follower.apply_update_proto(
10626                &project,
10627                pending_update.borrow_mut().take().unwrap(),
10628                window,
10629                cx,
10630            )
10631        })
10632        .unwrap()
10633        .await
10634        .unwrap();
10635    _ = follower.update(cx, |follower, _, cx| {
10636        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10637    });
10638    assert!(*is_still_following.borrow());
10639
10640    // Extend the pending selection so that it surrounds another selection
10641    _ = leader.update(cx, |leader, window, cx| {
10642        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10643    });
10644    follower
10645        .update(cx, |follower, window, cx| {
10646            follower.apply_update_proto(
10647                &project,
10648                pending_update.borrow_mut().take().unwrap(),
10649                window,
10650                cx,
10651            )
10652        })
10653        .unwrap()
10654        .await
10655        .unwrap();
10656    _ = follower.update(cx, |follower, _, cx| {
10657        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10658    });
10659
10660    // Scrolling locally breaks the follow
10661    _ = follower.update(cx, |follower, window, cx| {
10662        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10663        follower.set_scroll_anchor(
10664            ScrollAnchor {
10665                anchor: top_anchor,
10666                offset: gpui::Point::new(0.0, 0.5),
10667            },
10668            window,
10669            cx,
10670        );
10671    });
10672    assert!(!(*is_still_following.borrow()));
10673}
10674
10675#[gpui::test]
10676async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10677    init_test(cx, |_| {});
10678
10679    let fs = FakeFs::new(cx.executor());
10680    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10681    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10682    let pane = workspace
10683        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10684        .unwrap();
10685
10686    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10687
10688    let leader = pane.update_in(cx, |_, window, cx| {
10689        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10690        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10691    });
10692
10693    // Start following the editor when it has no excerpts.
10694    let mut state_message =
10695        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10696    let workspace_entity = workspace.root(cx).unwrap();
10697    let follower_1 = cx
10698        .update_window(*workspace.deref(), |_, window, cx| {
10699            Editor::from_state_proto(
10700                workspace_entity,
10701                ViewId {
10702                    creator: Default::default(),
10703                    id: 0,
10704                },
10705                &mut state_message,
10706                window,
10707                cx,
10708            )
10709        })
10710        .unwrap()
10711        .unwrap()
10712        .await
10713        .unwrap();
10714
10715    let update_message = Rc::new(RefCell::new(None));
10716    follower_1.update_in(cx, {
10717        let update = update_message.clone();
10718        |_, window, cx| {
10719            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10720                leader.read(cx).add_event_to_update_proto(
10721                    event,
10722                    &mut update.borrow_mut(),
10723                    window,
10724                    cx,
10725                );
10726            })
10727            .detach();
10728        }
10729    });
10730
10731    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10732        (
10733            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10734            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10735        )
10736    });
10737
10738    // Insert some excerpts.
10739    leader.update(cx, |leader, cx| {
10740        leader.buffer.update(cx, |multibuffer, cx| {
10741            let excerpt_ids = multibuffer.push_excerpts(
10742                buffer_1.clone(),
10743                [
10744                    ExcerptRange {
10745                        context: 1..6,
10746                        primary: None,
10747                    },
10748                    ExcerptRange {
10749                        context: 12..15,
10750                        primary: None,
10751                    },
10752                    ExcerptRange {
10753                        context: 0..3,
10754                        primary: None,
10755                    },
10756                ],
10757                cx,
10758            );
10759            multibuffer.insert_excerpts_after(
10760                excerpt_ids[0],
10761                buffer_2.clone(),
10762                [
10763                    ExcerptRange {
10764                        context: 8..12,
10765                        primary: None,
10766                    },
10767                    ExcerptRange {
10768                        context: 0..6,
10769                        primary: None,
10770                    },
10771                ],
10772                cx,
10773            );
10774        });
10775    });
10776
10777    // Apply the update of adding the excerpts.
10778    follower_1
10779        .update_in(cx, |follower, window, cx| {
10780            follower.apply_update_proto(
10781                &project,
10782                update_message.borrow().clone().unwrap(),
10783                window,
10784                cx,
10785            )
10786        })
10787        .await
10788        .unwrap();
10789    assert_eq!(
10790        follower_1.update(cx, |editor, cx| editor.text(cx)),
10791        leader.update(cx, |editor, cx| editor.text(cx))
10792    );
10793    update_message.borrow_mut().take();
10794
10795    // Start following separately after it already has excerpts.
10796    let mut state_message =
10797        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10798    let workspace_entity = workspace.root(cx).unwrap();
10799    let follower_2 = cx
10800        .update_window(*workspace.deref(), |_, window, cx| {
10801            Editor::from_state_proto(
10802                workspace_entity,
10803                ViewId {
10804                    creator: Default::default(),
10805                    id: 0,
10806                },
10807                &mut state_message,
10808                window,
10809                cx,
10810            )
10811        })
10812        .unwrap()
10813        .unwrap()
10814        .await
10815        .unwrap();
10816    assert_eq!(
10817        follower_2.update(cx, |editor, cx| editor.text(cx)),
10818        leader.update(cx, |editor, cx| editor.text(cx))
10819    );
10820
10821    // Remove some excerpts.
10822    leader.update(cx, |leader, cx| {
10823        leader.buffer.update(cx, |multibuffer, cx| {
10824            let excerpt_ids = multibuffer.excerpt_ids();
10825            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10826            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10827        });
10828    });
10829
10830    // Apply the update of removing the excerpts.
10831    follower_1
10832        .update_in(cx, |follower, window, cx| {
10833            follower.apply_update_proto(
10834                &project,
10835                update_message.borrow().clone().unwrap(),
10836                window,
10837                cx,
10838            )
10839        })
10840        .await
10841        .unwrap();
10842    follower_2
10843        .update_in(cx, |follower, window, cx| {
10844            follower.apply_update_proto(
10845                &project,
10846                update_message.borrow().clone().unwrap(),
10847                window,
10848                cx,
10849            )
10850        })
10851        .await
10852        .unwrap();
10853    update_message.borrow_mut().take();
10854    assert_eq!(
10855        follower_1.update(cx, |editor, cx| editor.text(cx)),
10856        leader.update(cx, |editor, cx| editor.text(cx))
10857    );
10858}
10859
10860#[gpui::test]
10861async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
10862    init_test(cx, |_| {});
10863
10864    let mut cx = EditorTestContext::new(cx).await;
10865    let lsp_store =
10866        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10867
10868    cx.set_state(indoc! {"
10869        ˇfn func(abc def: i32) -> u32 {
10870        }
10871    "});
10872
10873    cx.update(|_, cx| {
10874        lsp_store.update(cx, |lsp_store, cx| {
10875            lsp_store
10876                .update_diagnostics(
10877                    LanguageServerId(0),
10878                    lsp::PublishDiagnosticsParams {
10879                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10880                        version: None,
10881                        diagnostics: vec![
10882                            lsp::Diagnostic {
10883                                range: lsp::Range::new(
10884                                    lsp::Position::new(0, 11),
10885                                    lsp::Position::new(0, 12),
10886                                ),
10887                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10888                                ..Default::default()
10889                            },
10890                            lsp::Diagnostic {
10891                                range: lsp::Range::new(
10892                                    lsp::Position::new(0, 12),
10893                                    lsp::Position::new(0, 15),
10894                                ),
10895                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10896                                ..Default::default()
10897                            },
10898                            lsp::Diagnostic {
10899                                range: lsp::Range::new(
10900                                    lsp::Position::new(0, 25),
10901                                    lsp::Position::new(0, 28),
10902                                ),
10903                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10904                                ..Default::default()
10905                            },
10906                        ],
10907                    },
10908                    &[],
10909                    cx,
10910                )
10911                .unwrap()
10912        });
10913    });
10914
10915    executor.run_until_parked();
10916
10917    cx.update_editor(|editor, window, cx| {
10918        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10919    });
10920
10921    cx.assert_editor_state(indoc! {"
10922        fn func(abc def: i32) -> ˇu32 {
10923        }
10924    "});
10925
10926    cx.update_editor(|editor, window, cx| {
10927        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10928    });
10929
10930    cx.assert_editor_state(indoc! {"
10931        fn func(abc ˇdef: i32) -> u32 {
10932        }
10933    "});
10934
10935    cx.update_editor(|editor, window, cx| {
10936        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10937    });
10938
10939    cx.assert_editor_state(indoc! {"
10940        fn func(abcˇ def: i32) -> u32 {
10941        }
10942    "});
10943
10944    cx.update_editor(|editor, window, cx| {
10945        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10946    });
10947
10948    cx.assert_editor_state(indoc! {"
10949        fn func(abc def: i32) -> ˇu32 {
10950        }
10951    "});
10952}
10953
10954#[gpui::test]
10955async fn cycle_through_same_place_diagnostics(
10956    executor: BackgroundExecutor,
10957    cx: &mut TestAppContext,
10958) {
10959    init_test(cx, |_| {});
10960
10961    let mut cx = EditorTestContext::new(cx).await;
10962    let lsp_store =
10963        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10964
10965    cx.set_state(indoc! {"
10966        ˇfn func(abc def: i32) -> u32 {
10967        }
10968    "});
10969
10970    cx.update(|_, cx| {
10971        lsp_store.update(cx, |lsp_store, cx| {
10972            lsp_store
10973                .update_diagnostics(
10974                    LanguageServerId(0),
10975                    lsp::PublishDiagnosticsParams {
10976                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10977                        version: None,
10978                        diagnostics: vec![
10979                            lsp::Diagnostic {
10980                                range: lsp::Range::new(
10981                                    lsp::Position::new(0, 11),
10982                                    lsp::Position::new(0, 12),
10983                                ),
10984                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10985                                ..Default::default()
10986                            },
10987                            lsp::Diagnostic {
10988                                range: lsp::Range::new(
10989                                    lsp::Position::new(0, 12),
10990                                    lsp::Position::new(0, 15),
10991                                ),
10992                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10993                                ..Default::default()
10994                            },
10995                            lsp::Diagnostic {
10996                                range: lsp::Range::new(
10997                                    lsp::Position::new(0, 12),
10998                                    lsp::Position::new(0, 15),
10999                                ),
11000                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11001                                ..Default::default()
11002                            },
11003                            lsp::Diagnostic {
11004                                range: lsp::Range::new(
11005                                    lsp::Position::new(0, 25),
11006                                    lsp::Position::new(0, 28),
11007                                ),
11008                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11009                                ..Default::default()
11010                            },
11011                        ],
11012                    },
11013                    &[],
11014                    cx,
11015                )
11016                .unwrap()
11017        });
11018    });
11019    executor.run_until_parked();
11020
11021    //// Backward
11022
11023    // Fourth diagnostic
11024    cx.update_editor(|editor, window, cx| {
11025        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11026    });
11027    cx.assert_editor_state(indoc! {"
11028        fn func(abc def: i32) -> ˇu32 {
11029        }
11030    "});
11031
11032    // Third diagnostic
11033    cx.update_editor(|editor, window, cx| {
11034        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11035    });
11036    cx.assert_editor_state(indoc! {"
11037        fn func(abc ˇdef: i32) -> u32 {
11038        }
11039    "});
11040
11041    // Second diagnostic, same place
11042    cx.update_editor(|editor, window, cx| {
11043        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11044    });
11045    cx.assert_editor_state(indoc! {"
11046        fn func(abc ˇdef: i32) -> u32 {
11047        }
11048    "});
11049
11050    // First diagnostic
11051    cx.update_editor(|editor, window, cx| {
11052        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11053    });
11054    cx.assert_editor_state(indoc! {"
11055        fn func(abcˇ def: i32) -> u32 {
11056        }
11057    "});
11058
11059    // Wrapped over, fourth diagnostic
11060    cx.update_editor(|editor, window, cx| {
11061        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11062    });
11063    cx.assert_editor_state(indoc! {"
11064        fn func(abc def: i32) -> ˇu32 {
11065        }
11066    "});
11067
11068    cx.update_editor(|editor, window, cx| {
11069        editor.move_to_beginning(&MoveToBeginning, window, cx);
11070    });
11071    cx.assert_editor_state(indoc! {"
11072        ˇfn func(abc def: i32) -> u32 {
11073        }
11074    "});
11075
11076    //// Forward
11077
11078    // First diagnostic
11079    cx.update_editor(|editor, window, cx| {
11080        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11081    });
11082    cx.assert_editor_state(indoc! {"
11083        fn func(abcˇ def: i32) -> u32 {
11084        }
11085    "});
11086
11087    // Second diagnostic
11088    cx.update_editor(|editor, window, cx| {
11089        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11090    });
11091    cx.assert_editor_state(indoc! {"
11092        fn func(abc ˇdef: i32) -> u32 {
11093        }
11094    "});
11095
11096    // Third diagnostic, same place
11097    cx.update_editor(|editor, window, cx| {
11098        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11099    });
11100    cx.assert_editor_state(indoc! {"
11101        fn func(abc ˇdef: i32) -> u32 {
11102        }
11103    "});
11104
11105    // Fourth diagnostic
11106    cx.update_editor(|editor, window, cx| {
11107        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11108    });
11109    cx.assert_editor_state(indoc! {"
11110        fn func(abc def: i32) -> ˇu32 {
11111        }
11112    "});
11113
11114    // Wrapped around, first diagnostic
11115    cx.update_editor(|editor, window, cx| {
11116        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11117    });
11118    cx.assert_editor_state(indoc! {"
11119        fn func(abcˇ def: i32) -> u32 {
11120        }
11121    "});
11122}
11123
11124#[gpui::test]
11125async fn active_diagnostics_dismiss_after_invalidation(
11126    executor: BackgroundExecutor,
11127    cx: &mut TestAppContext,
11128) {
11129    init_test(cx, |_| {});
11130
11131    let mut cx = EditorTestContext::new(cx).await;
11132    let lsp_store =
11133        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11134
11135    cx.set_state(indoc! {"
11136        ˇfn func(abc def: i32) -> u32 {
11137        }
11138    "});
11139
11140    let message = "Something's wrong!";
11141    cx.update(|_, cx| {
11142        lsp_store.update(cx, |lsp_store, cx| {
11143            lsp_store
11144                .update_diagnostics(
11145                    LanguageServerId(0),
11146                    lsp::PublishDiagnosticsParams {
11147                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11148                        version: None,
11149                        diagnostics: vec![lsp::Diagnostic {
11150                            range: lsp::Range::new(
11151                                lsp::Position::new(0, 11),
11152                                lsp::Position::new(0, 12),
11153                            ),
11154                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11155                            message: message.to_string(),
11156                            ..Default::default()
11157                        }],
11158                    },
11159                    &[],
11160                    cx,
11161                )
11162                .unwrap()
11163        });
11164    });
11165    executor.run_until_parked();
11166
11167    cx.update_editor(|editor, window, cx| {
11168        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11169        assert_eq!(
11170            editor
11171                .active_diagnostics
11172                .as_ref()
11173                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11174            Some(message),
11175            "Should have a diagnostics group activated"
11176        );
11177    });
11178    cx.assert_editor_state(indoc! {"
11179        fn func(abcˇ def: i32) -> u32 {
11180        }
11181    "});
11182
11183    cx.update(|_, cx| {
11184        lsp_store.update(cx, |lsp_store, cx| {
11185            lsp_store
11186                .update_diagnostics(
11187                    LanguageServerId(0),
11188                    lsp::PublishDiagnosticsParams {
11189                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11190                        version: None,
11191                        diagnostics: Vec::new(),
11192                    },
11193                    &[],
11194                    cx,
11195                )
11196                .unwrap()
11197        });
11198    });
11199    executor.run_until_parked();
11200    cx.update_editor(|editor, _, _| {
11201        assert_eq!(
11202            editor.active_diagnostics, None,
11203            "After no diagnostics set to the editor, no diagnostics should be active"
11204        );
11205    });
11206    cx.assert_editor_state(indoc! {"
11207        fn func(abcˇ def: i32) -> u32 {
11208        }
11209    "});
11210
11211    cx.update_editor(|editor, window, cx| {
11212        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11213        assert_eq!(
11214            editor.active_diagnostics, None,
11215            "Should be no diagnostics to go to and activate"
11216        );
11217    });
11218    cx.assert_editor_state(indoc! {"
11219        fn func(abcˇ def: i32) -> u32 {
11220        }
11221    "});
11222}
11223
11224#[gpui::test]
11225async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11226    init_test(cx, |_| {});
11227
11228    let mut cx = EditorTestContext::new(cx).await;
11229
11230    cx.set_state(indoc! {"
11231        fn func(abˇc def: i32) -> u32 {
11232        }
11233    "});
11234    let lsp_store =
11235        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11236
11237    cx.update(|_, cx| {
11238        lsp_store.update(cx, |lsp_store, cx| {
11239            lsp_store.update_diagnostics(
11240                LanguageServerId(0),
11241                lsp::PublishDiagnosticsParams {
11242                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11243                    version: None,
11244                    diagnostics: vec![lsp::Diagnostic {
11245                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11246                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11247                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11248                        ..Default::default()
11249                    }],
11250                },
11251                &[],
11252                cx,
11253            )
11254        })
11255    }).unwrap();
11256    cx.run_until_parked();
11257    cx.update_editor(|editor, window, cx| {
11258        hover_popover::hover(editor, &Default::default(), window, cx)
11259    });
11260    cx.run_until_parked();
11261    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11262}
11263
11264#[gpui::test]
11265async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11266    init_test(cx, |_| {});
11267
11268    let mut cx = EditorTestContext::new(cx).await;
11269
11270    let diff_base = r#"
11271        use some::mod;
11272
11273        const A: u32 = 42;
11274
11275        fn main() {
11276            println!("hello");
11277
11278            println!("world");
11279        }
11280        "#
11281    .unindent();
11282
11283    // Edits are modified, removed, modified, added
11284    cx.set_state(
11285        &r#"
11286        use some::modified;
11287
11288        ˇ
11289        fn main() {
11290            println!("hello there");
11291
11292            println!("around the");
11293            println!("world");
11294        }
11295        "#
11296        .unindent(),
11297    );
11298
11299    cx.set_head_text(&diff_base);
11300    executor.run_until_parked();
11301
11302    cx.update_editor(|editor, window, cx| {
11303        //Wrap around the bottom of the buffer
11304        for _ in 0..3 {
11305            editor.go_to_next_hunk(&GoToHunk, window, cx);
11306        }
11307    });
11308
11309    cx.assert_editor_state(
11310        &r#"
11311        ˇuse some::modified;
11312
11313
11314        fn main() {
11315            println!("hello there");
11316
11317            println!("around the");
11318            println!("world");
11319        }
11320        "#
11321        .unindent(),
11322    );
11323
11324    cx.update_editor(|editor, window, cx| {
11325        //Wrap around the top of the buffer
11326        for _ in 0..2 {
11327            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11328        }
11329    });
11330
11331    cx.assert_editor_state(
11332        &r#"
11333        use some::modified;
11334
11335
11336        fn main() {
11337        ˇ    println!("hello there");
11338
11339            println!("around the");
11340            println!("world");
11341        }
11342        "#
11343        .unindent(),
11344    );
11345
11346    cx.update_editor(|editor, window, cx| {
11347        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11348    });
11349
11350    cx.assert_editor_state(
11351        &r#"
11352        use some::modified;
11353
11354        ˇ
11355        fn main() {
11356            println!("hello there");
11357
11358            println!("around the");
11359            println!("world");
11360        }
11361        "#
11362        .unindent(),
11363    );
11364
11365    cx.update_editor(|editor, window, cx| {
11366        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11367    });
11368
11369    cx.assert_editor_state(
11370        &r#"
11371        ˇuse some::modified;
11372
11373
11374        fn main() {
11375            println!("hello there");
11376
11377            println!("around the");
11378            println!("world");
11379        }
11380        "#
11381        .unindent(),
11382    );
11383
11384    cx.update_editor(|editor, window, cx| {
11385        for _ in 0..2 {
11386            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11387        }
11388    });
11389
11390    cx.assert_editor_state(
11391        &r#"
11392        use some::modified;
11393
11394
11395        fn main() {
11396        ˇ    println!("hello there");
11397
11398            println!("around the");
11399            println!("world");
11400        }
11401        "#
11402        .unindent(),
11403    );
11404
11405    cx.update_editor(|editor, window, cx| {
11406        editor.fold(&Fold, window, cx);
11407    });
11408
11409    cx.update_editor(|editor, window, cx| {
11410        editor.go_to_next_hunk(&GoToHunk, window, cx);
11411    });
11412
11413    cx.assert_editor_state(
11414        &r#"
11415        ˇuse some::modified;
11416
11417
11418        fn main() {
11419            println!("hello there");
11420
11421            println!("around the");
11422            println!("world");
11423        }
11424        "#
11425        .unindent(),
11426    );
11427}
11428
11429#[test]
11430fn test_split_words() {
11431    fn split(text: &str) -> Vec<&str> {
11432        split_words(text).collect()
11433    }
11434
11435    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11436    assert_eq!(split("hello_world"), &["hello_", "world"]);
11437    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11438    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11439    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11440    assert_eq!(split("helloworld"), &["helloworld"]);
11441
11442    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11443}
11444
11445#[gpui::test]
11446async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11447    init_test(cx, |_| {});
11448
11449    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11450    let mut assert = |before, after| {
11451        let _state_context = cx.set_state(before);
11452        cx.run_until_parked();
11453        cx.update_editor(|editor, window, cx| {
11454            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11455        });
11456        cx.assert_editor_state(after);
11457    };
11458
11459    // Outside bracket jumps to outside of matching bracket
11460    assert("console.logˇ(var);", "console.log(var)ˇ;");
11461    assert("console.log(var)ˇ;", "console.logˇ(var);");
11462
11463    // Inside bracket jumps to inside of matching bracket
11464    assert("console.log(ˇvar);", "console.log(varˇ);");
11465    assert("console.log(varˇ);", "console.log(ˇvar);");
11466
11467    // When outside a bracket and inside, favor jumping to the inside bracket
11468    assert(
11469        "console.log('foo', [1, 2, 3]ˇ);",
11470        "console.log(ˇ'foo', [1, 2, 3]);",
11471    );
11472    assert(
11473        "console.log(ˇ'foo', [1, 2, 3]);",
11474        "console.log('foo', [1, 2, 3]ˇ);",
11475    );
11476
11477    // Bias forward if two options are equally likely
11478    assert(
11479        "let result = curried_fun()ˇ();",
11480        "let result = curried_fun()()ˇ;",
11481    );
11482
11483    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11484    assert(
11485        indoc! {"
11486            function test() {
11487                console.log('test')ˇ
11488            }"},
11489        indoc! {"
11490            function test() {
11491                console.logˇ('test')
11492            }"},
11493    );
11494}
11495
11496#[gpui::test]
11497async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11498    init_test(cx, |_| {});
11499
11500    let fs = FakeFs::new(cx.executor());
11501    fs.insert_tree(
11502        path!("/a"),
11503        json!({
11504            "main.rs": "fn main() { let a = 5; }",
11505            "other.rs": "// Test file",
11506        }),
11507    )
11508    .await;
11509    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11510
11511    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11512    language_registry.add(Arc::new(Language::new(
11513        LanguageConfig {
11514            name: "Rust".into(),
11515            matcher: LanguageMatcher {
11516                path_suffixes: vec!["rs".to_string()],
11517                ..Default::default()
11518            },
11519            brackets: BracketPairConfig {
11520                pairs: vec![BracketPair {
11521                    start: "{".to_string(),
11522                    end: "}".to_string(),
11523                    close: true,
11524                    surround: true,
11525                    newline: true,
11526                }],
11527                disabled_scopes_by_bracket_ix: Vec::new(),
11528            },
11529            ..Default::default()
11530        },
11531        Some(tree_sitter_rust::LANGUAGE.into()),
11532    )));
11533    let mut fake_servers = language_registry.register_fake_lsp(
11534        "Rust",
11535        FakeLspAdapter {
11536            capabilities: lsp::ServerCapabilities {
11537                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11538                    first_trigger_character: "{".to_string(),
11539                    more_trigger_character: None,
11540                }),
11541                ..Default::default()
11542            },
11543            ..Default::default()
11544        },
11545    );
11546
11547    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11548
11549    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11550
11551    let worktree_id = workspace
11552        .update(cx, |workspace, _, cx| {
11553            workspace.project().update(cx, |project, cx| {
11554                project.worktrees(cx).next().unwrap().read(cx).id()
11555            })
11556        })
11557        .unwrap();
11558
11559    let buffer = project
11560        .update(cx, |project, cx| {
11561            project.open_local_buffer(path!("/a/main.rs"), cx)
11562        })
11563        .await
11564        .unwrap();
11565    let editor_handle = workspace
11566        .update(cx, |workspace, window, cx| {
11567            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11568        })
11569        .unwrap()
11570        .await
11571        .unwrap()
11572        .downcast::<Editor>()
11573        .unwrap();
11574
11575    cx.executor().start_waiting();
11576    let fake_server = fake_servers.next().await.unwrap();
11577
11578    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11579        assert_eq!(
11580            params.text_document_position.text_document.uri,
11581            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11582        );
11583        assert_eq!(
11584            params.text_document_position.position,
11585            lsp::Position::new(0, 21),
11586        );
11587
11588        Ok(Some(vec![lsp::TextEdit {
11589            new_text: "]".to_string(),
11590            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11591        }]))
11592    });
11593
11594    editor_handle.update_in(cx, |editor, window, cx| {
11595        window.focus(&editor.focus_handle(cx));
11596        editor.change_selections(None, window, cx, |s| {
11597            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11598        });
11599        editor.handle_input("{", window, cx);
11600    });
11601
11602    cx.executor().run_until_parked();
11603
11604    buffer.update(cx, |buffer, _| {
11605        assert_eq!(
11606            buffer.text(),
11607            "fn main() { let a = {5}; }",
11608            "No extra braces from on type formatting should appear in the buffer"
11609        )
11610    });
11611}
11612
11613#[gpui::test]
11614async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11615    init_test(cx, |_| {});
11616
11617    let fs = FakeFs::new(cx.executor());
11618    fs.insert_tree(
11619        path!("/a"),
11620        json!({
11621            "main.rs": "fn main() { let a = 5; }",
11622            "other.rs": "// Test file",
11623        }),
11624    )
11625    .await;
11626
11627    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11628
11629    let server_restarts = Arc::new(AtomicUsize::new(0));
11630    let closure_restarts = Arc::clone(&server_restarts);
11631    let language_server_name = "test language server";
11632    let language_name: LanguageName = "Rust".into();
11633
11634    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11635    language_registry.add(Arc::new(Language::new(
11636        LanguageConfig {
11637            name: language_name.clone(),
11638            matcher: LanguageMatcher {
11639                path_suffixes: vec!["rs".to_string()],
11640                ..Default::default()
11641            },
11642            ..Default::default()
11643        },
11644        Some(tree_sitter_rust::LANGUAGE.into()),
11645    )));
11646    let mut fake_servers = language_registry.register_fake_lsp(
11647        "Rust",
11648        FakeLspAdapter {
11649            name: language_server_name,
11650            initialization_options: Some(json!({
11651                "testOptionValue": true
11652            })),
11653            initializer: Some(Box::new(move |fake_server| {
11654                let task_restarts = Arc::clone(&closure_restarts);
11655                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11656                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11657                    futures::future::ready(Ok(()))
11658                });
11659            })),
11660            ..Default::default()
11661        },
11662    );
11663
11664    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11665    let _buffer = project
11666        .update(cx, |project, cx| {
11667            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11668        })
11669        .await
11670        .unwrap();
11671    let _fake_server = fake_servers.next().await.unwrap();
11672    update_test_language_settings(cx, |language_settings| {
11673        language_settings.languages.insert(
11674            language_name.clone(),
11675            LanguageSettingsContent {
11676                tab_size: NonZeroU32::new(8),
11677                ..Default::default()
11678            },
11679        );
11680    });
11681    cx.executor().run_until_parked();
11682    assert_eq!(
11683        server_restarts.load(atomic::Ordering::Acquire),
11684        0,
11685        "Should not restart LSP server on an unrelated change"
11686    );
11687
11688    update_test_project_settings(cx, |project_settings| {
11689        project_settings.lsp.insert(
11690            "Some other server name".into(),
11691            LspSettings {
11692                binary: None,
11693                settings: None,
11694                initialization_options: Some(json!({
11695                    "some other init value": false
11696                })),
11697            },
11698        );
11699    });
11700    cx.executor().run_until_parked();
11701    assert_eq!(
11702        server_restarts.load(atomic::Ordering::Acquire),
11703        0,
11704        "Should not restart LSP server on an unrelated LSP settings change"
11705    );
11706
11707    update_test_project_settings(cx, |project_settings| {
11708        project_settings.lsp.insert(
11709            language_server_name.into(),
11710            LspSettings {
11711                binary: None,
11712                settings: None,
11713                initialization_options: Some(json!({
11714                    "anotherInitValue": false
11715                })),
11716            },
11717        );
11718    });
11719    cx.executor().run_until_parked();
11720    assert_eq!(
11721        server_restarts.load(atomic::Ordering::Acquire),
11722        1,
11723        "Should restart LSP server on a related LSP settings change"
11724    );
11725
11726    update_test_project_settings(cx, |project_settings| {
11727        project_settings.lsp.insert(
11728            language_server_name.into(),
11729            LspSettings {
11730                binary: None,
11731                settings: None,
11732                initialization_options: Some(json!({
11733                    "anotherInitValue": false
11734                })),
11735            },
11736        );
11737    });
11738    cx.executor().run_until_parked();
11739    assert_eq!(
11740        server_restarts.load(atomic::Ordering::Acquire),
11741        1,
11742        "Should not restart LSP server on a related LSP settings change that is the same"
11743    );
11744
11745    update_test_project_settings(cx, |project_settings| {
11746        project_settings.lsp.insert(
11747            language_server_name.into(),
11748            LspSettings {
11749                binary: None,
11750                settings: None,
11751                initialization_options: None,
11752            },
11753        );
11754    });
11755    cx.executor().run_until_parked();
11756    assert_eq!(
11757        server_restarts.load(atomic::Ordering::Acquire),
11758        2,
11759        "Should restart LSP server on another related LSP settings change"
11760    );
11761}
11762
11763#[gpui::test]
11764async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11765    init_test(cx, |_| {});
11766
11767    let mut cx = EditorLspTestContext::new_rust(
11768        lsp::ServerCapabilities {
11769            completion_provider: Some(lsp::CompletionOptions {
11770                trigger_characters: Some(vec![".".to_string()]),
11771                resolve_provider: Some(true),
11772                ..Default::default()
11773            }),
11774            ..Default::default()
11775        },
11776        cx,
11777    )
11778    .await;
11779
11780    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11781    cx.simulate_keystroke(".");
11782    let completion_item = lsp::CompletionItem {
11783        label: "some".into(),
11784        kind: Some(lsp::CompletionItemKind::SNIPPET),
11785        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11786        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11787            kind: lsp::MarkupKind::Markdown,
11788            value: "```rust\nSome(2)\n```".to_string(),
11789        })),
11790        deprecated: Some(false),
11791        sort_text: Some("fffffff2".to_string()),
11792        filter_text: Some("some".to_string()),
11793        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11794        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11795            range: lsp::Range {
11796                start: lsp::Position {
11797                    line: 0,
11798                    character: 22,
11799                },
11800                end: lsp::Position {
11801                    line: 0,
11802                    character: 22,
11803                },
11804            },
11805            new_text: "Some(2)".to_string(),
11806        })),
11807        additional_text_edits: Some(vec![lsp::TextEdit {
11808            range: lsp::Range {
11809                start: lsp::Position {
11810                    line: 0,
11811                    character: 20,
11812                },
11813                end: lsp::Position {
11814                    line: 0,
11815                    character: 22,
11816                },
11817            },
11818            new_text: "".to_string(),
11819        }]),
11820        ..Default::default()
11821    };
11822
11823    let closure_completion_item = completion_item.clone();
11824    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11825        let task_completion_item = closure_completion_item.clone();
11826        async move {
11827            Ok(Some(lsp::CompletionResponse::Array(vec![
11828                task_completion_item,
11829            ])))
11830        }
11831    });
11832
11833    request.next().await;
11834
11835    cx.condition(|editor, _| editor.context_menu_visible())
11836        .await;
11837    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11838        editor
11839            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11840            .unwrap()
11841    });
11842    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11843
11844    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11845        let task_completion_item = completion_item.clone();
11846        async move { Ok(task_completion_item) }
11847    })
11848    .next()
11849    .await
11850    .unwrap();
11851    apply_additional_edits.await.unwrap();
11852    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11853}
11854
11855#[gpui::test]
11856async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11857    init_test(cx, |_| {});
11858
11859    let mut cx = EditorLspTestContext::new_rust(
11860        lsp::ServerCapabilities {
11861            completion_provider: Some(lsp::CompletionOptions {
11862                trigger_characters: Some(vec![".".to_string()]),
11863                resolve_provider: Some(true),
11864                ..Default::default()
11865            }),
11866            ..Default::default()
11867        },
11868        cx,
11869    )
11870    .await;
11871
11872    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11873    cx.simulate_keystroke(".");
11874
11875    let item1 = lsp::CompletionItem {
11876        label: "method id()".to_string(),
11877        filter_text: Some("id".to_string()),
11878        detail: None,
11879        documentation: None,
11880        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11881            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11882            new_text: ".id".to_string(),
11883        })),
11884        ..lsp::CompletionItem::default()
11885    };
11886
11887    let item2 = lsp::CompletionItem {
11888        label: "other".to_string(),
11889        filter_text: Some("other".to_string()),
11890        detail: None,
11891        documentation: None,
11892        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11893            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11894            new_text: ".other".to_string(),
11895        })),
11896        ..lsp::CompletionItem::default()
11897    };
11898
11899    let item1 = item1.clone();
11900    cx.handle_request::<lsp::request::Completion, _, _>({
11901        let item1 = item1.clone();
11902        move |_, _, _| {
11903            let item1 = item1.clone();
11904            let item2 = item2.clone();
11905            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11906        }
11907    })
11908    .next()
11909    .await;
11910
11911    cx.condition(|editor, _| editor.context_menu_visible())
11912        .await;
11913    cx.update_editor(|editor, _, _| {
11914        let context_menu = editor.context_menu.borrow_mut();
11915        let context_menu = context_menu
11916            .as_ref()
11917            .expect("Should have the context menu deployed");
11918        match context_menu {
11919            CodeContextMenu::Completions(completions_menu) => {
11920                let completions = completions_menu.completions.borrow_mut();
11921                assert_eq!(
11922                    completions
11923                        .iter()
11924                        .map(|completion| &completion.label.text)
11925                        .collect::<Vec<_>>(),
11926                    vec!["method id()", "other"]
11927                )
11928            }
11929            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11930        }
11931    });
11932
11933    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11934        let item1 = item1.clone();
11935        move |_, item_to_resolve, _| {
11936            let item1 = item1.clone();
11937            async move {
11938                if item1 == item_to_resolve {
11939                    Ok(lsp::CompletionItem {
11940                        label: "method id()".to_string(),
11941                        filter_text: Some("id".to_string()),
11942                        detail: Some("Now resolved!".to_string()),
11943                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11944                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11945                            range: lsp::Range::new(
11946                                lsp::Position::new(0, 22),
11947                                lsp::Position::new(0, 22),
11948                            ),
11949                            new_text: ".id".to_string(),
11950                        })),
11951                        ..lsp::CompletionItem::default()
11952                    })
11953                } else {
11954                    Ok(item_to_resolve)
11955                }
11956            }
11957        }
11958    })
11959    .next()
11960    .await
11961    .unwrap();
11962    cx.run_until_parked();
11963
11964    cx.update_editor(|editor, window, cx| {
11965        editor.context_menu_next(&Default::default(), window, cx);
11966    });
11967
11968    cx.update_editor(|editor, _, _| {
11969        let context_menu = editor.context_menu.borrow_mut();
11970        let context_menu = context_menu
11971            .as_ref()
11972            .expect("Should have the context menu deployed");
11973        match context_menu {
11974            CodeContextMenu::Completions(completions_menu) => {
11975                let completions = completions_menu.completions.borrow_mut();
11976                assert_eq!(
11977                    completions
11978                        .iter()
11979                        .map(|completion| &completion.label.text)
11980                        .collect::<Vec<_>>(),
11981                    vec!["method id() Now resolved!", "other"],
11982                    "Should update first completion label, but not second as the filter text did not match."
11983                );
11984            }
11985            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11986        }
11987    });
11988}
11989
11990#[gpui::test]
11991async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
11992    init_test(cx, |_| {});
11993
11994    let mut cx = EditorLspTestContext::new_rust(
11995        lsp::ServerCapabilities {
11996            completion_provider: Some(lsp::CompletionOptions {
11997                trigger_characters: Some(vec![".".to_string()]),
11998                resolve_provider: Some(true),
11999                ..Default::default()
12000            }),
12001            ..Default::default()
12002        },
12003        cx,
12004    )
12005    .await;
12006
12007    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12008    cx.simulate_keystroke(".");
12009
12010    let unresolved_item_1 = lsp::CompletionItem {
12011        label: "id".to_string(),
12012        filter_text: Some("id".to_string()),
12013        detail: None,
12014        documentation: None,
12015        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12016            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12017            new_text: ".id".to_string(),
12018        })),
12019        ..lsp::CompletionItem::default()
12020    };
12021    let resolved_item_1 = lsp::CompletionItem {
12022        additional_text_edits: Some(vec![lsp::TextEdit {
12023            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12024            new_text: "!!".to_string(),
12025        }]),
12026        ..unresolved_item_1.clone()
12027    };
12028    let unresolved_item_2 = lsp::CompletionItem {
12029        label: "other".to_string(),
12030        filter_text: Some("other".to_string()),
12031        detail: None,
12032        documentation: None,
12033        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12034            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12035            new_text: ".other".to_string(),
12036        })),
12037        ..lsp::CompletionItem::default()
12038    };
12039    let resolved_item_2 = lsp::CompletionItem {
12040        additional_text_edits: Some(vec![lsp::TextEdit {
12041            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12042            new_text: "??".to_string(),
12043        }]),
12044        ..unresolved_item_2.clone()
12045    };
12046
12047    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12048    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12049    cx.lsp
12050        .server
12051        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12052            let unresolved_item_1 = unresolved_item_1.clone();
12053            let resolved_item_1 = resolved_item_1.clone();
12054            let unresolved_item_2 = unresolved_item_2.clone();
12055            let resolved_item_2 = resolved_item_2.clone();
12056            let resolve_requests_1 = resolve_requests_1.clone();
12057            let resolve_requests_2 = resolve_requests_2.clone();
12058            move |unresolved_request, _| {
12059                let unresolved_item_1 = unresolved_item_1.clone();
12060                let resolved_item_1 = resolved_item_1.clone();
12061                let unresolved_item_2 = unresolved_item_2.clone();
12062                let resolved_item_2 = resolved_item_2.clone();
12063                let resolve_requests_1 = resolve_requests_1.clone();
12064                let resolve_requests_2 = resolve_requests_2.clone();
12065                async move {
12066                    if unresolved_request == unresolved_item_1 {
12067                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12068                        Ok(resolved_item_1.clone())
12069                    } else if unresolved_request == unresolved_item_2 {
12070                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12071                        Ok(resolved_item_2.clone())
12072                    } else {
12073                        panic!("Unexpected completion item {unresolved_request:?}")
12074                    }
12075                }
12076            }
12077        })
12078        .detach();
12079
12080    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12081        let unresolved_item_1 = unresolved_item_1.clone();
12082        let unresolved_item_2 = unresolved_item_2.clone();
12083        async move {
12084            Ok(Some(lsp::CompletionResponse::Array(vec![
12085                unresolved_item_1,
12086                unresolved_item_2,
12087            ])))
12088        }
12089    })
12090    .next()
12091    .await;
12092
12093    cx.condition(|editor, _| editor.context_menu_visible())
12094        .await;
12095    cx.update_editor(|editor, _, _| {
12096        let context_menu = editor.context_menu.borrow_mut();
12097        let context_menu = context_menu
12098            .as_ref()
12099            .expect("Should have the context menu deployed");
12100        match context_menu {
12101            CodeContextMenu::Completions(completions_menu) => {
12102                let completions = completions_menu.completions.borrow_mut();
12103                assert_eq!(
12104                    completions
12105                        .iter()
12106                        .map(|completion| &completion.label.text)
12107                        .collect::<Vec<_>>(),
12108                    vec!["id", "other"]
12109                )
12110            }
12111            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12112        }
12113    });
12114    cx.run_until_parked();
12115
12116    cx.update_editor(|editor, window, cx| {
12117        editor.context_menu_next(&ContextMenuNext, window, cx);
12118    });
12119    cx.run_until_parked();
12120    cx.update_editor(|editor, window, cx| {
12121        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12122    });
12123    cx.run_until_parked();
12124    cx.update_editor(|editor, window, cx| {
12125        editor.context_menu_next(&ContextMenuNext, window, cx);
12126    });
12127    cx.run_until_parked();
12128    cx.update_editor(|editor, window, cx| {
12129        editor
12130            .compose_completion(&ComposeCompletion::default(), window, cx)
12131            .expect("No task returned")
12132    })
12133    .await
12134    .expect("Completion failed");
12135    cx.run_until_parked();
12136
12137    cx.update_editor(|editor, _, cx| {
12138        assert_eq!(
12139            resolve_requests_1.load(atomic::Ordering::Acquire),
12140            1,
12141            "Should always resolve once despite multiple selections"
12142        );
12143        assert_eq!(
12144            resolve_requests_2.load(atomic::Ordering::Acquire),
12145            1,
12146            "Should always resolve once after multiple selections and applying the completion"
12147        );
12148        assert_eq!(
12149            editor.text(cx),
12150            "fn main() { let a = ??.other; }",
12151            "Should use resolved data when applying the completion"
12152        );
12153    });
12154}
12155
12156#[gpui::test]
12157async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12158    init_test(cx, |_| {});
12159
12160    let item_0 = lsp::CompletionItem {
12161        label: "abs".into(),
12162        insert_text: Some("abs".into()),
12163        data: Some(json!({ "very": "special"})),
12164        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12165        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12166            lsp::InsertReplaceEdit {
12167                new_text: "abs".to_string(),
12168                insert: lsp::Range::default(),
12169                replace: lsp::Range::default(),
12170            },
12171        )),
12172        ..lsp::CompletionItem::default()
12173    };
12174    let items = iter::once(item_0.clone())
12175        .chain((11..51).map(|i| lsp::CompletionItem {
12176            label: format!("item_{}", i),
12177            insert_text: Some(format!("item_{}", i)),
12178            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12179            ..lsp::CompletionItem::default()
12180        }))
12181        .collect::<Vec<_>>();
12182
12183    let default_commit_characters = vec!["?".to_string()];
12184    let default_data = json!({ "default": "data"});
12185    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12186    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12187    let default_edit_range = lsp::Range {
12188        start: lsp::Position {
12189            line: 0,
12190            character: 5,
12191        },
12192        end: lsp::Position {
12193            line: 0,
12194            character: 5,
12195        },
12196    };
12197
12198    let item_0_out = lsp::CompletionItem {
12199        commit_characters: Some(default_commit_characters.clone()),
12200        insert_text_format: Some(default_insert_text_format),
12201        ..item_0
12202    };
12203    let items_out = iter::once(item_0_out)
12204        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
12205            commit_characters: Some(default_commit_characters.clone()),
12206            data: Some(default_data.clone()),
12207            insert_text_mode: Some(default_insert_text_mode),
12208            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12209                range: default_edit_range,
12210                new_text: item.label.clone(),
12211            })),
12212            ..item.clone()
12213        }))
12214        .collect::<Vec<lsp::CompletionItem>>();
12215
12216    let mut cx = EditorLspTestContext::new_rust(
12217        lsp::ServerCapabilities {
12218            completion_provider: Some(lsp::CompletionOptions {
12219                trigger_characters: Some(vec![".".to_string()]),
12220                resolve_provider: Some(true),
12221                ..Default::default()
12222            }),
12223            ..Default::default()
12224        },
12225        cx,
12226    )
12227    .await;
12228
12229    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12230    cx.simulate_keystroke(".");
12231
12232    let completion_data = default_data.clone();
12233    let completion_characters = default_commit_characters.clone();
12234    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12235        let default_data = completion_data.clone();
12236        let default_commit_characters = completion_characters.clone();
12237        let items = items.clone();
12238        async move {
12239            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12240                items,
12241                item_defaults: Some(lsp::CompletionListItemDefaults {
12242                    data: Some(default_data.clone()),
12243                    commit_characters: Some(default_commit_characters.clone()),
12244                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12245                        default_edit_range,
12246                    )),
12247                    insert_text_format: Some(default_insert_text_format),
12248                    insert_text_mode: Some(default_insert_text_mode),
12249                }),
12250                ..lsp::CompletionList::default()
12251            })))
12252        }
12253    })
12254    .next()
12255    .await;
12256
12257    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12258    cx.lsp
12259        .server
12260        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12261            let closure_resolved_items = resolved_items.clone();
12262            move |item_to_resolve, _| {
12263                let closure_resolved_items = closure_resolved_items.clone();
12264                async move {
12265                    closure_resolved_items.lock().push(item_to_resolve.clone());
12266                    Ok(item_to_resolve)
12267                }
12268            }
12269        })
12270        .detach();
12271
12272    cx.condition(|editor, _| editor.context_menu_visible())
12273        .await;
12274    cx.run_until_parked();
12275    cx.update_editor(|editor, _, _| {
12276        let menu = editor.context_menu.borrow_mut();
12277        match menu.as_ref().expect("should have the completions menu") {
12278            CodeContextMenu::Completions(completions_menu) => {
12279                assert_eq!(
12280                    completions_menu
12281                        .entries
12282                        .borrow()
12283                        .iter()
12284                        .map(|mat| mat.string.clone())
12285                        .collect::<Vec<String>>(),
12286                    items_out
12287                        .iter()
12288                        .map(|completion| completion.label.clone())
12289                        .collect::<Vec<String>>()
12290                );
12291            }
12292            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12293        }
12294    });
12295    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12296    // with 4 from the end.
12297    assert_eq!(
12298        *resolved_items.lock(),
12299        [
12300            &items_out[0..16],
12301            &items_out[items_out.len() - 4..items_out.len()]
12302        ]
12303        .concat()
12304        .iter()
12305        .cloned()
12306        .collect::<Vec<lsp::CompletionItem>>()
12307    );
12308    resolved_items.lock().clear();
12309
12310    cx.update_editor(|editor, window, cx| {
12311        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12312    });
12313    cx.run_until_parked();
12314    // Completions that have already been resolved are skipped.
12315    assert_eq!(
12316        *resolved_items.lock(),
12317        items_out[items_out.len() - 16..items_out.len() - 4]
12318            .iter()
12319            .cloned()
12320            .collect::<Vec<lsp::CompletionItem>>()
12321    );
12322    resolved_items.lock().clear();
12323}
12324
12325#[gpui::test]
12326async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12327    init_test(cx, |_| {});
12328
12329    let mut cx = EditorLspTestContext::new(
12330        Language::new(
12331            LanguageConfig {
12332                matcher: LanguageMatcher {
12333                    path_suffixes: vec!["jsx".into()],
12334                    ..Default::default()
12335                },
12336                overrides: [(
12337                    "element".into(),
12338                    LanguageConfigOverride {
12339                        word_characters: Override::Set(['-'].into_iter().collect()),
12340                        ..Default::default()
12341                    },
12342                )]
12343                .into_iter()
12344                .collect(),
12345                ..Default::default()
12346            },
12347            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12348        )
12349        .with_override_query("(jsx_self_closing_element) @element")
12350        .unwrap(),
12351        lsp::ServerCapabilities {
12352            completion_provider: Some(lsp::CompletionOptions {
12353                trigger_characters: Some(vec![":".to_string()]),
12354                ..Default::default()
12355            }),
12356            ..Default::default()
12357        },
12358        cx,
12359    )
12360    .await;
12361
12362    cx.lsp
12363        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12364            Ok(Some(lsp::CompletionResponse::Array(vec![
12365                lsp::CompletionItem {
12366                    label: "bg-blue".into(),
12367                    ..Default::default()
12368                },
12369                lsp::CompletionItem {
12370                    label: "bg-red".into(),
12371                    ..Default::default()
12372                },
12373                lsp::CompletionItem {
12374                    label: "bg-yellow".into(),
12375                    ..Default::default()
12376                },
12377            ])))
12378        });
12379
12380    cx.set_state(r#"<p class="bgˇ" />"#);
12381
12382    // Trigger completion when typing a dash, because the dash is an extra
12383    // word character in the 'element' scope, which contains the cursor.
12384    cx.simulate_keystroke("-");
12385    cx.executor().run_until_parked();
12386    cx.update_editor(|editor, _, _| {
12387        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12388        {
12389            assert_eq!(
12390                completion_menu_entries(&menu),
12391                &["bg-red", "bg-blue", "bg-yellow"]
12392            );
12393        } else {
12394            panic!("expected completion menu to be open");
12395        }
12396    });
12397
12398    cx.simulate_keystroke("l");
12399    cx.executor().run_until_parked();
12400    cx.update_editor(|editor, _, _| {
12401        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12402        {
12403            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12404        } else {
12405            panic!("expected completion menu to be open");
12406        }
12407    });
12408
12409    // When filtering completions, consider the character after the '-' to
12410    // be the start of a subword.
12411    cx.set_state(r#"<p class="yelˇ" />"#);
12412    cx.simulate_keystroke("l");
12413    cx.executor().run_until_parked();
12414    cx.update_editor(|editor, _, _| {
12415        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12416        {
12417            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12418        } else {
12419            panic!("expected completion menu to be open");
12420        }
12421    });
12422}
12423
12424fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12425    let entries = menu.entries.borrow();
12426    entries.iter().map(|mat| mat.string.clone()).collect()
12427}
12428
12429#[gpui::test]
12430async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12431    init_test(cx, |settings| {
12432        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12433            FormatterList(vec![Formatter::Prettier].into()),
12434        ))
12435    });
12436
12437    let fs = FakeFs::new(cx.executor());
12438    fs.insert_file(path!("/file.ts"), Default::default()).await;
12439
12440    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12441    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12442
12443    language_registry.add(Arc::new(Language::new(
12444        LanguageConfig {
12445            name: "TypeScript".into(),
12446            matcher: LanguageMatcher {
12447                path_suffixes: vec!["ts".to_string()],
12448                ..Default::default()
12449            },
12450            ..Default::default()
12451        },
12452        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12453    )));
12454    update_test_language_settings(cx, |settings| {
12455        settings.defaults.prettier = Some(PrettierSettings {
12456            allowed: true,
12457            ..PrettierSettings::default()
12458        });
12459    });
12460
12461    let test_plugin = "test_plugin";
12462    let _ = language_registry.register_fake_lsp(
12463        "TypeScript",
12464        FakeLspAdapter {
12465            prettier_plugins: vec![test_plugin],
12466            ..Default::default()
12467        },
12468    );
12469
12470    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12471    let buffer = project
12472        .update(cx, |project, cx| {
12473            project.open_local_buffer(path!("/file.ts"), cx)
12474        })
12475        .await
12476        .unwrap();
12477
12478    let buffer_text = "one\ntwo\nthree\n";
12479    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12480    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12481    editor.update_in(cx, |editor, window, cx| {
12482        editor.set_text(buffer_text, window, cx)
12483    });
12484
12485    editor
12486        .update_in(cx, |editor, window, cx| {
12487            editor.perform_format(
12488                project.clone(),
12489                FormatTrigger::Manual,
12490                FormatTarget::Buffers,
12491                window,
12492                cx,
12493            )
12494        })
12495        .unwrap()
12496        .await;
12497    assert_eq!(
12498        editor.update(cx, |editor, cx| editor.text(cx)),
12499        buffer_text.to_string() + prettier_format_suffix,
12500        "Test prettier formatting was not applied to the original buffer text",
12501    );
12502
12503    update_test_language_settings(cx, |settings| {
12504        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12505    });
12506    let format = editor.update_in(cx, |editor, window, cx| {
12507        editor.perform_format(
12508            project.clone(),
12509            FormatTrigger::Manual,
12510            FormatTarget::Buffers,
12511            window,
12512            cx,
12513        )
12514    });
12515    format.await.unwrap();
12516    assert_eq!(
12517        editor.update(cx, |editor, cx| editor.text(cx)),
12518        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12519        "Autoformatting (via test prettier) was not applied to the original buffer text",
12520    );
12521}
12522
12523#[gpui::test]
12524async fn test_addition_reverts(cx: &mut TestAppContext) {
12525    init_test(cx, |_| {});
12526    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12527    let base_text = indoc! {r#"
12528        struct Row;
12529        struct Row1;
12530        struct Row2;
12531
12532        struct Row4;
12533        struct Row5;
12534        struct Row6;
12535
12536        struct Row8;
12537        struct Row9;
12538        struct Row10;"#};
12539
12540    // When addition hunks are not adjacent to carets, no hunk revert is performed
12541    assert_hunk_revert(
12542        indoc! {r#"struct Row;
12543                   struct Row1;
12544                   struct Row1.1;
12545                   struct Row1.2;
12546                   struct Row2;ˇ
12547
12548                   struct Row4;
12549                   struct Row5;
12550                   struct Row6;
12551
12552                   struct Row8;
12553                   ˇstruct Row9;
12554                   struct Row9.1;
12555                   struct Row9.2;
12556                   struct Row9.3;
12557                   struct Row10;"#},
12558        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12559        indoc! {r#"struct Row;
12560                   struct Row1;
12561                   struct Row1.1;
12562                   struct Row1.2;
12563                   struct Row2;ˇ
12564
12565                   struct Row4;
12566                   struct Row5;
12567                   struct Row6;
12568
12569                   struct Row8;
12570                   ˇstruct Row9;
12571                   struct Row9.1;
12572                   struct Row9.2;
12573                   struct Row9.3;
12574                   struct Row10;"#},
12575        base_text,
12576        &mut cx,
12577    );
12578    // Same for selections
12579    assert_hunk_revert(
12580        indoc! {r#"struct Row;
12581                   struct Row1;
12582                   struct Row2;
12583                   struct Row2.1;
12584                   struct Row2.2;
12585                   «ˇ
12586                   struct Row4;
12587                   struct» Row5;
12588                   «struct Row6;
12589                   ˇ»
12590                   struct Row9.1;
12591                   struct Row9.2;
12592                   struct Row9.3;
12593                   struct Row8;
12594                   struct Row9;
12595                   struct Row10;"#},
12596        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12597        indoc! {r#"struct Row;
12598                   struct Row1;
12599                   struct Row2;
12600                   struct Row2.1;
12601                   struct Row2.2;
12602                   «ˇ
12603                   struct Row4;
12604                   struct» Row5;
12605                   «struct Row6;
12606                   ˇ»
12607                   struct Row9.1;
12608                   struct Row9.2;
12609                   struct Row9.3;
12610                   struct Row8;
12611                   struct Row9;
12612                   struct Row10;"#},
12613        base_text,
12614        &mut cx,
12615    );
12616
12617    // When carets and selections intersect the addition hunks, those are reverted.
12618    // Adjacent carets got merged.
12619    assert_hunk_revert(
12620        indoc! {r#"struct Row;
12621                   ˇ// something on the top
12622                   struct Row1;
12623                   struct Row2;
12624                   struct Roˇw3.1;
12625                   struct Row2.2;
12626                   struct Row2.3;ˇ
12627
12628                   struct Row4;
12629                   struct ˇRow5.1;
12630                   struct Row5.2;
12631                   struct «Rowˇ»5.3;
12632                   struct Row5;
12633                   struct Row6;
12634                   ˇ
12635                   struct Row9.1;
12636                   struct «Rowˇ»9.2;
12637                   struct «ˇRow»9.3;
12638                   struct Row8;
12639                   struct Row9;
12640                   «ˇ// something on bottom»
12641                   struct Row10;"#},
12642        vec![
12643            DiffHunkStatus::added_none(),
12644            DiffHunkStatus::added_none(),
12645            DiffHunkStatus::added_none(),
12646            DiffHunkStatus::added_none(),
12647            DiffHunkStatus::added_none(),
12648        ],
12649        indoc! {r#"struct Row;
12650                   ˇstruct Row1;
12651                   struct Row2;
12652                   ˇ
12653                   struct Row4;
12654                   ˇstruct Row5;
12655                   struct Row6;
12656                   ˇ
12657                   ˇstruct Row8;
12658                   struct Row9;
12659                   ˇstruct Row10;"#},
12660        base_text,
12661        &mut cx,
12662    );
12663}
12664
12665#[gpui::test]
12666async fn test_modification_reverts(cx: &mut TestAppContext) {
12667    init_test(cx, |_| {});
12668    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12669    let base_text = indoc! {r#"
12670        struct Row;
12671        struct Row1;
12672        struct Row2;
12673
12674        struct Row4;
12675        struct Row5;
12676        struct Row6;
12677
12678        struct Row8;
12679        struct Row9;
12680        struct Row10;"#};
12681
12682    // Modification hunks behave the same as the addition ones.
12683    assert_hunk_revert(
12684        indoc! {r#"struct Row;
12685                   struct Row1;
12686                   struct Row33;
12687                   ˇ
12688                   struct Row4;
12689                   struct Row5;
12690                   struct Row6;
12691                   ˇ
12692                   struct Row99;
12693                   struct Row9;
12694                   struct Row10;"#},
12695        vec![
12696            DiffHunkStatus::modified_none(),
12697            DiffHunkStatus::modified_none(),
12698        ],
12699        indoc! {r#"struct Row;
12700                   struct Row1;
12701                   struct Row33;
12702                   ˇ
12703                   struct Row4;
12704                   struct Row5;
12705                   struct Row6;
12706                   ˇ
12707                   struct Row99;
12708                   struct Row9;
12709                   struct Row10;"#},
12710        base_text,
12711        &mut cx,
12712    );
12713    assert_hunk_revert(
12714        indoc! {r#"struct Row;
12715                   struct Row1;
12716                   struct Row33;
12717                   «ˇ
12718                   struct Row4;
12719                   struct» Row5;
12720                   «struct Row6;
12721                   ˇ»
12722                   struct Row99;
12723                   struct Row9;
12724                   struct Row10;"#},
12725        vec![
12726            DiffHunkStatus::modified_none(),
12727            DiffHunkStatus::modified_none(),
12728        ],
12729        indoc! {r#"struct Row;
12730                   struct Row1;
12731                   struct Row33;
12732                   «ˇ
12733                   struct Row4;
12734                   struct» Row5;
12735                   «struct Row6;
12736                   ˇ»
12737                   struct Row99;
12738                   struct Row9;
12739                   struct Row10;"#},
12740        base_text,
12741        &mut cx,
12742    );
12743
12744    assert_hunk_revert(
12745        indoc! {r#"ˇstruct Row1.1;
12746                   struct Row1;
12747                   «ˇstr»uct Row22;
12748
12749                   struct ˇRow44;
12750                   struct Row5;
12751                   struct «Rˇ»ow66;ˇ
12752
12753                   «struˇ»ct Row88;
12754                   struct Row9;
12755                   struct Row1011;ˇ"#},
12756        vec![
12757            DiffHunkStatus::modified_none(),
12758            DiffHunkStatus::modified_none(),
12759            DiffHunkStatus::modified_none(),
12760            DiffHunkStatus::modified_none(),
12761            DiffHunkStatus::modified_none(),
12762            DiffHunkStatus::modified_none(),
12763        ],
12764        indoc! {r#"struct Row;
12765                   ˇstruct Row1;
12766                   struct Row2;
12767                   ˇ
12768                   struct Row4;
12769                   ˇstruct Row5;
12770                   struct Row6;
12771                   ˇ
12772                   struct Row8;
12773                   ˇstruct Row9;
12774                   struct Row10;ˇ"#},
12775        base_text,
12776        &mut cx,
12777    );
12778}
12779
12780#[gpui::test]
12781async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12782    init_test(cx, |_| {});
12783    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12784    let base_text = indoc! {r#"
12785        one
12786
12787        two
12788        three
12789        "#};
12790
12791    cx.set_head_text(base_text);
12792    cx.set_state("\nˇ\n");
12793    cx.executor().run_until_parked();
12794    cx.update_editor(|editor, _window, cx| {
12795        editor.expand_selected_diff_hunks(cx);
12796    });
12797    cx.executor().run_until_parked();
12798    cx.update_editor(|editor, window, cx| {
12799        editor.backspace(&Default::default(), window, cx);
12800    });
12801    cx.run_until_parked();
12802    cx.assert_state_with_diff(
12803        indoc! {r#"
12804
12805        - two
12806        - threeˇ
12807        +
12808        "#}
12809        .to_string(),
12810    );
12811}
12812
12813#[gpui::test]
12814async fn test_deletion_reverts(cx: &mut TestAppContext) {
12815    init_test(cx, |_| {});
12816    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12817    let base_text = indoc! {r#"struct Row;
12818struct Row1;
12819struct Row2;
12820
12821struct Row4;
12822struct Row5;
12823struct Row6;
12824
12825struct Row8;
12826struct Row9;
12827struct Row10;"#};
12828
12829    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12830    assert_hunk_revert(
12831        indoc! {r#"struct Row;
12832                   struct Row2;
12833
12834                   ˇstruct Row4;
12835                   struct Row5;
12836                   struct Row6;
12837                   ˇ
12838                   struct Row8;
12839                   struct Row10;"#},
12840        vec![
12841            DiffHunkStatus::deleted_none(),
12842            DiffHunkStatus::deleted_none(),
12843        ],
12844        indoc! {r#"struct Row;
12845                   struct Row2;
12846
12847                   ˇstruct Row4;
12848                   struct Row5;
12849                   struct Row6;
12850                   ˇ
12851                   struct Row8;
12852                   struct Row10;"#},
12853        base_text,
12854        &mut cx,
12855    );
12856    assert_hunk_revert(
12857        indoc! {r#"struct Row;
12858                   struct Row2;
12859
12860                   «ˇstruct Row4;
12861                   struct» Row5;
12862                   «struct Row6;
12863                   ˇ»
12864                   struct Row8;
12865                   struct Row10;"#},
12866        vec![
12867            DiffHunkStatus::deleted_none(),
12868            DiffHunkStatus::deleted_none(),
12869        ],
12870        indoc! {r#"struct Row;
12871                   struct Row2;
12872
12873                   «ˇstruct Row4;
12874                   struct» Row5;
12875                   «struct Row6;
12876                   ˇ»
12877                   struct Row8;
12878                   struct Row10;"#},
12879        base_text,
12880        &mut cx,
12881    );
12882
12883    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12884    assert_hunk_revert(
12885        indoc! {r#"struct Row;
12886                   ˇstruct Row2;
12887
12888                   struct Row4;
12889                   struct Row5;
12890                   struct Row6;
12891
12892                   struct Row8;ˇ
12893                   struct Row10;"#},
12894        vec![
12895            DiffHunkStatus::deleted_none(),
12896            DiffHunkStatus::deleted_none(),
12897        ],
12898        indoc! {r#"struct Row;
12899                   struct Row1;
12900                   ˇstruct Row2;
12901
12902                   struct Row4;
12903                   struct Row5;
12904                   struct Row6;
12905
12906                   struct Row8;ˇ
12907                   struct Row9;
12908                   struct Row10;"#},
12909        base_text,
12910        &mut cx,
12911    );
12912    assert_hunk_revert(
12913        indoc! {r#"struct Row;
12914                   struct Row2«ˇ;
12915                   struct Row4;
12916                   struct» Row5;
12917                   «struct Row6;
12918
12919                   struct Row8;ˇ»
12920                   struct Row10;"#},
12921        vec![
12922            DiffHunkStatus::deleted_none(),
12923            DiffHunkStatus::deleted_none(),
12924            DiffHunkStatus::deleted_none(),
12925        ],
12926        indoc! {r#"struct Row;
12927                   struct Row1;
12928                   struct Row2«ˇ;
12929
12930                   struct Row4;
12931                   struct» Row5;
12932                   «struct Row6;
12933
12934                   struct Row8;ˇ»
12935                   struct Row9;
12936                   struct Row10;"#},
12937        base_text,
12938        &mut cx,
12939    );
12940}
12941
12942#[gpui::test]
12943async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
12944    init_test(cx, |_| {});
12945
12946    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12947    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12948    let base_text_3 =
12949        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12950
12951    let text_1 = edit_first_char_of_every_line(base_text_1);
12952    let text_2 = edit_first_char_of_every_line(base_text_2);
12953    let text_3 = edit_first_char_of_every_line(base_text_3);
12954
12955    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12956    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12957    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12958
12959    let multibuffer = cx.new(|cx| {
12960        let mut multibuffer = MultiBuffer::new(ReadWrite);
12961        multibuffer.push_excerpts(
12962            buffer_1.clone(),
12963            [
12964                ExcerptRange {
12965                    context: Point::new(0, 0)..Point::new(3, 0),
12966                    primary: None,
12967                },
12968                ExcerptRange {
12969                    context: Point::new(5, 0)..Point::new(7, 0),
12970                    primary: None,
12971                },
12972                ExcerptRange {
12973                    context: Point::new(9, 0)..Point::new(10, 4),
12974                    primary: None,
12975                },
12976            ],
12977            cx,
12978        );
12979        multibuffer.push_excerpts(
12980            buffer_2.clone(),
12981            [
12982                ExcerptRange {
12983                    context: Point::new(0, 0)..Point::new(3, 0),
12984                    primary: None,
12985                },
12986                ExcerptRange {
12987                    context: Point::new(5, 0)..Point::new(7, 0),
12988                    primary: None,
12989                },
12990                ExcerptRange {
12991                    context: Point::new(9, 0)..Point::new(10, 4),
12992                    primary: None,
12993                },
12994            ],
12995            cx,
12996        );
12997        multibuffer.push_excerpts(
12998            buffer_3.clone(),
12999            [
13000                ExcerptRange {
13001                    context: Point::new(0, 0)..Point::new(3, 0),
13002                    primary: None,
13003                },
13004                ExcerptRange {
13005                    context: Point::new(5, 0)..Point::new(7, 0),
13006                    primary: None,
13007                },
13008                ExcerptRange {
13009                    context: Point::new(9, 0)..Point::new(10, 4),
13010                    primary: None,
13011                },
13012            ],
13013            cx,
13014        );
13015        multibuffer
13016    });
13017
13018    let fs = FakeFs::new(cx.executor());
13019    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13020    let (editor, cx) = cx
13021        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13022    editor.update_in(cx, |editor, _window, cx| {
13023        for (buffer, diff_base) in [
13024            (buffer_1.clone(), base_text_1),
13025            (buffer_2.clone(), base_text_2),
13026            (buffer_3.clone(), base_text_3),
13027        ] {
13028            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13029            editor
13030                .buffer
13031                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13032        }
13033    });
13034    cx.executor().run_until_parked();
13035
13036    editor.update_in(cx, |editor, window, cx| {
13037        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}");
13038        editor.select_all(&SelectAll, window, cx);
13039        editor.git_restore(&Default::default(), window, cx);
13040    });
13041    cx.executor().run_until_parked();
13042
13043    // When all ranges are selected, all buffer hunks are reverted.
13044    editor.update(cx, |editor, cx| {
13045        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");
13046    });
13047    buffer_1.update(cx, |buffer, _| {
13048        assert_eq!(buffer.text(), base_text_1);
13049    });
13050    buffer_2.update(cx, |buffer, _| {
13051        assert_eq!(buffer.text(), base_text_2);
13052    });
13053    buffer_3.update(cx, |buffer, _| {
13054        assert_eq!(buffer.text(), base_text_3);
13055    });
13056
13057    editor.update_in(cx, |editor, window, cx| {
13058        editor.undo(&Default::default(), window, cx);
13059    });
13060
13061    editor.update_in(cx, |editor, window, cx| {
13062        editor.change_selections(None, window, cx, |s| {
13063            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13064        });
13065        editor.git_restore(&Default::default(), window, cx);
13066    });
13067
13068    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13069    // but not affect buffer_2 and its related excerpts.
13070    editor.update(cx, |editor, cx| {
13071        assert_eq!(
13072            editor.text(cx),
13073            "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}"
13074        );
13075    });
13076    buffer_1.update(cx, |buffer, _| {
13077        assert_eq!(buffer.text(), base_text_1);
13078    });
13079    buffer_2.update(cx, |buffer, _| {
13080        assert_eq!(
13081            buffer.text(),
13082            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13083        );
13084    });
13085    buffer_3.update(cx, |buffer, _| {
13086        assert_eq!(
13087            buffer.text(),
13088            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13089        );
13090    });
13091
13092    fn edit_first_char_of_every_line(text: &str) -> String {
13093        text.split('\n')
13094            .map(|line| format!("X{}", &line[1..]))
13095            .collect::<Vec<_>>()
13096            .join("\n")
13097    }
13098}
13099
13100#[gpui::test]
13101async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13102    init_test(cx, |_| {});
13103
13104    let cols = 4;
13105    let rows = 10;
13106    let sample_text_1 = sample_text(rows, cols, 'a');
13107    assert_eq!(
13108        sample_text_1,
13109        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13110    );
13111    let sample_text_2 = sample_text(rows, cols, 'l');
13112    assert_eq!(
13113        sample_text_2,
13114        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13115    );
13116    let sample_text_3 = sample_text(rows, cols, 'v');
13117    assert_eq!(
13118        sample_text_3,
13119        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13120    );
13121
13122    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13123    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13124    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13125
13126    let multi_buffer = cx.new(|cx| {
13127        let mut multibuffer = MultiBuffer::new(ReadWrite);
13128        multibuffer.push_excerpts(
13129            buffer_1.clone(),
13130            [
13131                ExcerptRange {
13132                    context: Point::new(0, 0)..Point::new(3, 0),
13133                    primary: None,
13134                },
13135                ExcerptRange {
13136                    context: Point::new(5, 0)..Point::new(7, 0),
13137                    primary: None,
13138                },
13139                ExcerptRange {
13140                    context: Point::new(9, 0)..Point::new(10, 4),
13141                    primary: None,
13142                },
13143            ],
13144            cx,
13145        );
13146        multibuffer.push_excerpts(
13147            buffer_2.clone(),
13148            [
13149                ExcerptRange {
13150                    context: Point::new(0, 0)..Point::new(3, 0),
13151                    primary: None,
13152                },
13153                ExcerptRange {
13154                    context: Point::new(5, 0)..Point::new(7, 0),
13155                    primary: None,
13156                },
13157                ExcerptRange {
13158                    context: Point::new(9, 0)..Point::new(10, 4),
13159                    primary: None,
13160                },
13161            ],
13162            cx,
13163        );
13164        multibuffer.push_excerpts(
13165            buffer_3.clone(),
13166            [
13167                ExcerptRange {
13168                    context: Point::new(0, 0)..Point::new(3, 0),
13169                    primary: None,
13170                },
13171                ExcerptRange {
13172                    context: Point::new(5, 0)..Point::new(7, 0),
13173                    primary: None,
13174                },
13175                ExcerptRange {
13176                    context: Point::new(9, 0)..Point::new(10, 4),
13177                    primary: None,
13178                },
13179            ],
13180            cx,
13181        );
13182        multibuffer
13183    });
13184
13185    let fs = FakeFs::new(cx.executor());
13186    fs.insert_tree(
13187        "/a",
13188        json!({
13189            "main.rs": sample_text_1,
13190            "other.rs": sample_text_2,
13191            "lib.rs": sample_text_3,
13192        }),
13193    )
13194    .await;
13195    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13196    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13197    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13198    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13199        Editor::new(
13200            EditorMode::Full,
13201            multi_buffer,
13202            Some(project.clone()),
13203            true,
13204            window,
13205            cx,
13206        )
13207    });
13208    let multibuffer_item_id = workspace
13209        .update(cx, |workspace, window, cx| {
13210            assert!(
13211                workspace.active_item(cx).is_none(),
13212                "active item should be None before the first item is added"
13213            );
13214            workspace.add_item_to_active_pane(
13215                Box::new(multi_buffer_editor.clone()),
13216                None,
13217                true,
13218                window,
13219                cx,
13220            );
13221            let active_item = workspace
13222                .active_item(cx)
13223                .expect("should have an active item after adding the multi buffer");
13224            assert!(
13225                !active_item.is_singleton(cx),
13226                "A multi buffer was expected to active after adding"
13227            );
13228            active_item.item_id()
13229        })
13230        .unwrap();
13231    cx.executor().run_until_parked();
13232
13233    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13234        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13235            s.select_ranges(Some(1..2))
13236        });
13237        editor.open_excerpts(&OpenExcerpts, window, cx);
13238    });
13239    cx.executor().run_until_parked();
13240    let first_item_id = workspace
13241        .update(cx, |workspace, window, cx| {
13242            let active_item = workspace
13243                .active_item(cx)
13244                .expect("should have an active item after navigating into the 1st buffer");
13245            let first_item_id = active_item.item_id();
13246            assert_ne!(
13247                first_item_id, multibuffer_item_id,
13248                "Should navigate into the 1st buffer and activate it"
13249            );
13250            assert!(
13251                active_item.is_singleton(cx),
13252                "New active item should be a singleton buffer"
13253            );
13254            assert_eq!(
13255                active_item
13256                    .act_as::<Editor>(cx)
13257                    .expect("should have navigated into an editor for the 1st buffer")
13258                    .read(cx)
13259                    .text(cx),
13260                sample_text_1
13261            );
13262
13263            workspace
13264                .go_back(workspace.active_pane().downgrade(), window, cx)
13265                .detach_and_log_err(cx);
13266
13267            first_item_id
13268        })
13269        .unwrap();
13270    cx.executor().run_until_parked();
13271    workspace
13272        .update(cx, |workspace, _, cx| {
13273            let active_item = workspace
13274                .active_item(cx)
13275                .expect("should have an active item after navigating back");
13276            assert_eq!(
13277                active_item.item_id(),
13278                multibuffer_item_id,
13279                "Should navigate back to the multi buffer"
13280            );
13281            assert!(!active_item.is_singleton(cx));
13282        })
13283        .unwrap();
13284
13285    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13286        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13287            s.select_ranges(Some(39..40))
13288        });
13289        editor.open_excerpts(&OpenExcerpts, window, cx);
13290    });
13291    cx.executor().run_until_parked();
13292    let second_item_id = workspace
13293        .update(cx, |workspace, window, cx| {
13294            let active_item = workspace
13295                .active_item(cx)
13296                .expect("should have an active item after navigating into the 2nd buffer");
13297            let second_item_id = active_item.item_id();
13298            assert_ne!(
13299                second_item_id, multibuffer_item_id,
13300                "Should navigate away from the multibuffer"
13301            );
13302            assert_ne!(
13303                second_item_id, first_item_id,
13304                "Should navigate into the 2nd buffer and activate it"
13305            );
13306            assert!(
13307                active_item.is_singleton(cx),
13308                "New active item should be a singleton buffer"
13309            );
13310            assert_eq!(
13311                active_item
13312                    .act_as::<Editor>(cx)
13313                    .expect("should have navigated into an editor")
13314                    .read(cx)
13315                    .text(cx),
13316                sample_text_2
13317            );
13318
13319            workspace
13320                .go_back(workspace.active_pane().downgrade(), window, cx)
13321                .detach_and_log_err(cx);
13322
13323            second_item_id
13324        })
13325        .unwrap();
13326    cx.executor().run_until_parked();
13327    workspace
13328        .update(cx, |workspace, _, cx| {
13329            let active_item = workspace
13330                .active_item(cx)
13331                .expect("should have an active item after navigating back from the 2nd buffer");
13332            assert_eq!(
13333                active_item.item_id(),
13334                multibuffer_item_id,
13335                "Should navigate back from the 2nd buffer to the multi buffer"
13336            );
13337            assert!(!active_item.is_singleton(cx));
13338        })
13339        .unwrap();
13340
13341    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13342        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13343            s.select_ranges(Some(70..70))
13344        });
13345        editor.open_excerpts(&OpenExcerpts, window, cx);
13346    });
13347    cx.executor().run_until_parked();
13348    workspace
13349        .update(cx, |workspace, window, cx| {
13350            let active_item = workspace
13351                .active_item(cx)
13352                .expect("should have an active item after navigating into the 3rd buffer");
13353            let third_item_id = active_item.item_id();
13354            assert_ne!(
13355                third_item_id, multibuffer_item_id,
13356                "Should navigate into the 3rd buffer and activate it"
13357            );
13358            assert_ne!(third_item_id, first_item_id);
13359            assert_ne!(third_item_id, second_item_id);
13360            assert!(
13361                active_item.is_singleton(cx),
13362                "New active item should be a singleton buffer"
13363            );
13364            assert_eq!(
13365                active_item
13366                    .act_as::<Editor>(cx)
13367                    .expect("should have navigated into an editor")
13368                    .read(cx)
13369                    .text(cx),
13370                sample_text_3
13371            );
13372
13373            workspace
13374                .go_back(workspace.active_pane().downgrade(), window, cx)
13375                .detach_and_log_err(cx);
13376        })
13377        .unwrap();
13378    cx.executor().run_until_parked();
13379    workspace
13380        .update(cx, |workspace, _, cx| {
13381            let active_item = workspace
13382                .active_item(cx)
13383                .expect("should have an active item after navigating back from the 3rd buffer");
13384            assert_eq!(
13385                active_item.item_id(),
13386                multibuffer_item_id,
13387                "Should navigate back from the 3rd buffer to the multi buffer"
13388            );
13389            assert!(!active_item.is_singleton(cx));
13390        })
13391        .unwrap();
13392}
13393
13394#[gpui::test]
13395async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13396    init_test(cx, |_| {});
13397
13398    let mut cx = EditorTestContext::new(cx).await;
13399
13400    let diff_base = r#"
13401        use some::mod;
13402
13403        const A: u32 = 42;
13404
13405        fn main() {
13406            println!("hello");
13407
13408            println!("world");
13409        }
13410        "#
13411    .unindent();
13412
13413    cx.set_state(
13414        &r#"
13415        use some::modified;
13416
13417        ˇ
13418        fn main() {
13419            println!("hello there");
13420
13421            println!("around the");
13422            println!("world");
13423        }
13424        "#
13425        .unindent(),
13426    );
13427
13428    cx.set_head_text(&diff_base);
13429    executor.run_until_parked();
13430
13431    cx.update_editor(|editor, window, cx| {
13432        editor.go_to_next_hunk(&GoToHunk, window, cx);
13433        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13434    });
13435    executor.run_until_parked();
13436    cx.assert_state_with_diff(
13437        r#"
13438          use some::modified;
13439
13440
13441          fn main() {
13442        -     println!("hello");
13443        + ˇ    println!("hello there");
13444
13445              println!("around the");
13446              println!("world");
13447          }
13448        "#
13449        .unindent(),
13450    );
13451
13452    cx.update_editor(|editor, window, cx| {
13453        for _ in 0..2 {
13454            editor.go_to_next_hunk(&GoToHunk, window, cx);
13455            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13456        }
13457    });
13458    executor.run_until_parked();
13459    cx.assert_state_with_diff(
13460        r#"
13461        - use some::mod;
13462        + ˇuse some::modified;
13463
13464
13465          fn main() {
13466        -     println!("hello");
13467        +     println!("hello there");
13468
13469        +     println!("around the");
13470              println!("world");
13471          }
13472        "#
13473        .unindent(),
13474    );
13475
13476    cx.update_editor(|editor, window, cx| {
13477        editor.go_to_next_hunk(&GoToHunk, window, cx);
13478        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13479    });
13480    executor.run_until_parked();
13481    cx.assert_state_with_diff(
13482        r#"
13483        - use some::mod;
13484        + use some::modified;
13485
13486        - const A: u32 = 42;
13487          ˇ
13488          fn main() {
13489        -     println!("hello");
13490        +     println!("hello there");
13491
13492        +     println!("around the");
13493              println!("world");
13494          }
13495        "#
13496        .unindent(),
13497    );
13498
13499    cx.update_editor(|editor, window, cx| {
13500        editor.cancel(&Cancel, window, cx);
13501    });
13502
13503    cx.assert_state_with_diff(
13504        r#"
13505          use some::modified;
13506
13507          ˇ
13508          fn main() {
13509              println!("hello there");
13510
13511              println!("around the");
13512              println!("world");
13513          }
13514        "#
13515        .unindent(),
13516    );
13517}
13518
13519#[gpui::test]
13520async fn test_diff_base_change_with_expanded_diff_hunks(
13521    executor: BackgroundExecutor,
13522    cx: &mut TestAppContext,
13523) {
13524    init_test(cx, |_| {});
13525
13526    let mut cx = EditorTestContext::new(cx).await;
13527
13528    let diff_base = r#"
13529        use some::mod1;
13530        use some::mod2;
13531
13532        const A: u32 = 42;
13533        const B: u32 = 42;
13534        const C: u32 = 42;
13535
13536        fn main() {
13537            println!("hello");
13538
13539            println!("world");
13540        }
13541        "#
13542    .unindent();
13543
13544    cx.set_state(
13545        &r#"
13546        use some::mod2;
13547
13548        const A: u32 = 42;
13549        const C: u32 = 42;
13550
13551        fn main(ˇ) {
13552            //println!("hello");
13553
13554            println!("world");
13555            //
13556            //
13557        }
13558        "#
13559        .unindent(),
13560    );
13561
13562    cx.set_head_text(&diff_base);
13563    executor.run_until_parked();
13564
13565    cx.update_editor(|editor, window, cx| {
13566        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13567    });
13568    executor.run_until_parked();
13569    cx.assert_state_with_diff(
13570        r#"
13571        - use some::mod1;
13572          use some::mod2;
13573
13574          const A: u32 = 42;
13575        - const B: u32 = 42;
13576          const C: u32 = 42;
13577
13578          fn main(ˇ) {
13579        -     println!("hello");
13580        +     //println!("hello");
13581
13582              println!("world");
13583        +     //
13584        +     //
13585          }
13586        "#
13587        .unindent(),
13588    );
13589
13590    cx.set_head_text("new diff base!");
13591    executor.run_until_parked();
13592    cx.assert_state_with_diff(
13593        r#"
13594        - new diff base!
13595        + use some::mod2;
13596        +
13597        + const A: u32 = 42;
13598        + const C: u32 = 42;
13599        +
13600        + fn main(ˇ) {
13601        +     //println!("hello");
13602        +
13603        +     println!("world");
13604        +     //
13605        +     //
13606        + }
13607        "#
13608        .unindent(),
13609    );
13610}
13611
13612#[gpui::test]
13613async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13614    init_test(cx, |_| {});
13615
13616    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13617    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13618    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13619    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13620    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13621    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13622
13623    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13624    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13625    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13626
13627    let multi_buffer = cx.new(|cx| {
13628        let mut multibuffer = MultiBuffer::new(ReadWrite);
13629        multibuffer.push_excerpts(
13630            buffer_1.clone(),
13631            [
13632                ExcerptRange {
13633                    context: Point::new(0, 0)..Point::new(3, 0),
13634                    primary: None,
13635                },
13636                ExcerptRange {
13637                    context: Point::new(5, 0)..Point::new(7, 0),
13638                    primary: None,
13639                },
13640                ExcerptRange {
13641                    context: Point::new(9, 0)..Point::new(10, 3),
13642                    primary: None,
13643                },
13644            ],
13645            cx,
13646        );
13647        multibuffer.push_excerpts(
13648            buffer_2.clone(),
13649            [
13650                ExcerptRange {
13651                    context: Point::new(0, 0)..Point::new(3, 0),
13652                    primary: None,
13653                },
13654                ExcerptRange {
13655                    context: Point::new(5, 0)..Point::new(7, 0),
13656                    primary: None,
13657                },
13658                ExcerptRange {
13659                    context: Point::new(9, 0)..Point::new(10, 3),
13660                    primary: None,
13661                },
13662            ],
13663            cx,
13664        );
13665        multibuffer.push_excerpts(
13666            buffer_3.clone(),
13667            [
13668                ExcerptRange {
13669                    context: Point::new(0, 0)..Point::new(3, 0),
13670                    primary: None,
13671                },
13672                ExcerptRange {
13673                    context: Point::new(5, 0)..Point::new(7, 0),
13674                    primary: None,
13675                },
13676                ExcerptRange {
13677                    context: Point::new(9, 0)..Point::new(10, 3),
13678                    primary: None,
13679                },
13680            ],
13681            cx,
13682        );
13683        multibuffer
13684    });
13685
13686    let editor = cx.add_window(|window, cx| {
13687        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13688    });
13689    editor
13690        .update(cx, |editor, _window, cx| {
13691            for (buffer, diff_base) in [
13692                (buffer_1.clone(), file_1_old),
13693                (buffer_2.clone(), file_2_old),
13694                (buffer_3.clone(), file_3_old),
13695            ] {
13696                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13697                editor
13698                    .buffer
13699                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13700            }
13701        })
13702        .unwrap();
13703
13704    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13705    cx.run_until_parked();
13706
13707    cx.assert_editor_state(
13708        &"
13709            ˇaaa
13710            ccc
13711            ddd
13712
13713            ggg
13714            hhh
13715
13716
13717            lll
13718            mmm
13719            NNN
13720
13721            qqq
13722            rrr
13723
13724            uuu
13725            111
13726            222
13727            333
13728
13729            666
13730            777
13731
13732            000
13733            !!!"
13734        .unindent(),
13735    );
13736
13737    cx.update_editor(|editor, window, cx| {
13738        editor.select_all(&SelectAll, window, cx);
13739        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13740    });
13741    cx.executor().run_until_parked();
13742
13743    cx.assert_state_with_diff(
13744        "
13745            «aaa
13746          - bbb
13747            ccc
13748            ddd
13749
13750            ggg
13751            hhh
13752
13753
13754            lll
13755            mmm
13756          - nnn
13757          + NNN
13758
13759            qqq
13760            rrr
13761
13762            uuu
13763            111
13764            222
13765            333
13766
13767          + 666
13768            777
13769
13770            000
13771            !!!ˇ»"
13772            .unindent(),
13773    );
13774}
13775
13776#[gpui::test]
13777async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13778    init_test(cx, |_| {});
13779
13780    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13781    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13782
13783    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13784    let multi_buffer = cx.new(|cx| {
13785        let mut multibuffer = MultiBuffer::new(ReadWrite);
13786        multibuffer.push_excerpts(
13787            buffer.clone(),
13788            [
13789                ExcerptRange {
13790                    context: Point::new(0, 0)..Point::new(2, 0),
13791                    primary: None,
13792                },
13793                ExcerptRange {
13794                    context: Point::new(4, 0)..Point::new(7, 0),
13795                    primary: None,
13796                },
13797                ExcerptRange {
13798                    context: Point::new(9, 0)..Point::new(10, 0),
13799                    primary: None,
13800                },
13801            ],
13802            cx,
13803        );
13804        multibuffer
13805    });
13806
13807    let editor = cx.add_window(|window, cx| {
13808        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13809    });
13810    editor
13811        .update(cx, |editor, _window, cx| {
13812            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13813            editor
13814                .buffer
13815                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13816        })
13817        .unwrap();
13818
13819    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13820    cx.run_until_parked();
13821
13822    cx.update_editor(|editor, window, cx| {
13823        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13824    });
13825    cx.executor().run_until_parked();
13826
13827    // When the start of a hunk coincides with the start of its excerpt,
13828    // the hunk is expanded. When the start of a a hunk is earlier than
13829    // the start of its excerpt, the hunk is not expanded.
13830    cx.assert_state_with_diff(
13831        "
13832            ˇaaa
13833          - bbb
13834          + BBB
13835
13836          - ddd
13837          - eee
13838          + DDD
13839          + EEE
13840            fff
13841
13842            iii
13843        "
13844        .unindent(),
13845    );
13846}
13847
13848#[gpui::test]
13849async fn test_edits_around_expanded_insertion_hunks(
13850    executor: BackgroundExecutor,
13851    cx: &mut TestAppContext,
13852) {
13853    init_test(cx, |_| {});
13854
13855    let mut cx = EditorTestContext::new(cx).await;
13856
13857    let diff_base = r#"
13858        use some::mod1;
13859        use some::mod2;
13860
13861        const A: u32 = 42;
13862
13863        fn main() {
13864            println!("hello");
13865
13866            println!("world");
13867        }
13868        "#
13869    .unindent();
13870    executor.run_until_parked();
13871    cx.set_state(
13872        &r#"
13873        use some::mod1;
13874        use some::mod2;
13875
13876        const A: u32 = 42;
13877        const B: u32 = 42;
13878        const C: u32 = 42;
13879        ˇ
13880
13881        fn main() {
13882            println!("hello");
13883
13884            println!("world");
13885        }
13886        "#
13887        .unindent(),
13888    );
13889
13890    cx.set_head_text(&diff_base);
13891    executor.run_until_parked();
13892
13893    cx.update_editor(|editor, window, cx| {
13894        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13895    });
13896    executor.run_until_parked();
13897
13898    cx.assert_state_with_diff(
13899        r#"
13900        use some::mod1;
13901        use some::mod2;
13902
13903        const A: u32 = 42;
13904      + const B: u32 = 42;
13905      + const C: u32 = 42;
13906      + ˇ
13907
13908        fn main() {
13909            println!("hello");
13910
13911            println!("world");
13912        }
13913      "#
13914        .unindent(),
13915    );
13916
13917    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13918    executor.run_until_parked();
13919
13920    cx.assert_state_with_diff(
13921        r#"
13922        use some::mod1;
13923        use some::mod2;
13924
13925        const A: u32 = 42;
13926      + const B: u32 = 42;
13927      + const C: u32 = 42;
13928      + const D: u32 = 42;
13929      + ˇ
13930
13931        fn main() {
13932            println!("hello");
13933
13934            println!("world");
13935        }
13936      "#
13937        .unindent(),
13938    );
13939
13940    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13941    executor.run_until_parked();
13942
13943    cx.assert_state_with_diff(
13944        r#"
13945        use some::mod1;
13946        use some::mod2;
13947
13948        const A: u32 = 42;
13949      + const B: u32 = 42;
13950      + const C: u32 = 42;
13951      + const D: u32 = 42;
13952      + const E: u32 = 42;
13953      + ˇ
13954
13955        fn main() {
13956            println!("hello");
13957
13958            println!("world");
13959        }
13960      "#
13961        .unindent(),
13962    );
13963
13964    cx.update_editor(|editor, window, cx| {
13965        editor.delete_line(&DeleteLine, window, cx);
13966    });
13967    executor.run_until_parked();
13968
13969    cx.assert_state_with_diff(
13970        r#"
13971        use some::mod1;
13972        use some::mod2;
13973
13974        const A: u32 = 42;
13975      + const B: u32 = 42;
13976      + const C: u32 = 42;
13977      + const D: u32 = 42;
13978      + const E: u32 = 42;
13979        ˇ
13980        fn main() {
13981            println!("hello");
13982
13983            println!("world");
13984        }
13985      "#
13986        .unindent(),
13987    );
13988
13989    cx.update_editor(|editor, window, cx| {
13990        editor.move_up(&MoveUp, window, cx);
13991        editor.delete_line(&DeleteLine, window, cx);
13992        editor.move_up(&MoveUp, window, cx);
13993        editor.delete_line(&DeleteLine, window, cx);
13994        editor.move_up(&MoveUp, window, cx);
13995        editor.delete_line(&DeleteLine, window, cx);
13996    });
13997    executor.run_until_parked();
13998    cx.assert_state_with_diff(
13999        r#"
14000        use some::mod1;
14001        use some::mod2;
14002
14003        const A: u32 = 42;
14004      + const B: u32 = 42;
14005        ˇ
14006        fn main() {
14007            println!("hello");
14008
14009            println!("world");
14010        }
14011      "#
14012        .unindent(),
14013    );
14014
14015    cx.update_editor(|editor, window, cx| {
14016        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14017        editor.delete_line(&DeleteLine, window, cx);
14018    });
14019    executor.run_until_parked();
14020    cx.assert_state_with_diff(
14021        r#"
14022        ˇ
14023        fn main() {
14024            println!("hello");
14025
14026            println!("world");
14027        }
14028      "#
14029        .unindent(),
14030    );
14031}
14032
14033#[gpui::test]
14034async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14035    init_test(cx, |_| {});
14036
14037    let mut cx = EditorTestContext::new(cx).await;
14038    cx.set_head_text(indoc! { "
14039        one
14040        two
14041        three
14042        four
14043        five
14044        "
14045    });
14046    cx.set_state(indoc! { "
14047        one
14048        ˇthree
14049        five
14050    "});
14051    cx.run_until_parked();
14052    cx.update_editor(|editor, window, cx| {
14053        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14054    });
14055    cx.assert_state_with_diff(
14056        indoc! { "
14057        one
14058      - two
14059        ˇthree
14060      - four
14061        five
14062    "}
14063        .to_string(),
14064    );
14065    cx.update_editor(|editor, window, cx| {
14066        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14067    });
14068
14069    cx.assert_state_with_diff(
14070        indoc! { "
14071        one
14072        ˇthree
14073        five
14074    "}
14075        .to_string(),
14076    );
14077
14078    cx.set_state(indoc! { "
14079        one
14080        ˇTWO
14081        three
14082        four
14083        five
14084    "});
14085    cx.run_until_parked();
14086    cx.update_editor(|editor, window, cx| {
14087        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14088    });
14089
14090    cx.assert_state_with_diff(
14091        indoc! { "
14092            one
14093          - two
14094          + ˇTWO
14095            three
14096            four
14097            five
14098        "}
14099        .to_string(),
14100    );
14101    cx.update_editor(|editor, window, cx| {
14102        editor.move_up(&Default::default(), window, cx);
14103        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14104    });
14105    cx.assert_state_with_diff(
14106        indoc! { "
14107            one
14108            ˇTWO
14109            three
14110            four
14111            five
14112        "}
14113        .to_string(),
14114    );
14115}
14116
14117#[gpui::test]
14118async fn test_edits_around_expanded_deletion_hunks(
14119    executor: BackgroundExecutor,
14120    cx: &mut TestAppContext,
14121) {
14122    init_test(cx, |_| {});
14123
14124    let mut cx = EditorTestContext::new(cx).await;
14125
14126    let diff_base = r#"
14127        use some::mod1;
14128        use some::mod2;
14129
14130        const A: u32 = 42;
14131        const B: u32 = 42;
14132        const C: u32 = 42;
14133
14134
14135        fn main() {
14136            println!("hello");
14137
14138            println!("world");
14139        }
14140    "#
14141    .unindent();
14142    executor.run_until_parked();
14143    cx.set_state(
14144        &r#"
14145        use some::mod1;
14146        use some::mod2;
14147
14148        ˇconst B: u32 = 42;
14149        const C: u32 = 42;
14150
14151
14152        fn main() {
14153            println!("hello");
14154
14155            println!("world");
14156        }
14157        "#
14158        .unindent(),
14159    );
14160
14161    cx.set_head_text(&diff_base);
14162    executor.run_until_parked();
14163
14164    cx.update_editor(|editor, window, cx| {
14165        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14166    });
14167    executor.run_until_parked();
14168
14169    cx.assert_state_with_diff(
14170        r#"
14171        use some::mod1;
14172        use some::mod2;
14173
14174      - const A: u32 = 42;
14175        ˇconst B: u32 = 42;
14176        const C: u32 = 42;
14177
14178
14179        fn main() {
14180            println!("hello");
14181
14182            println!("world");
14183        }
14184      "#
14185        .unindent(),
14186    );
14187
14188    cx.update_editor(|editor, window, cx| {
14189        editor.delete_line(&DeleteLine, window, cx);
14190    });
14191    executor.run_until_parked();
14192    cx.assert_state_with_diff(
14193        r#"
14194        use some::mod1;
14195        use some::mod2;
14196
14197      - const A: u32 = 42;
14198      - const B: u32 = 42;
14199        ˇconst C: u32 = 42;
14200
14201
14202        fn main() {
14203            println!("hello");
14204
14205            println!("world");
14206        }
14207      "#
14208        .unindent(),
14209    );
14210
14211    cx.update_editor(|editor, window, cx| {
14212        editor.delete_line(&DeleteLine, window, cx);
14213    });
14214    executor.run_until_parked();
14215    cx.assert_state_with_diff(
14216        r#"
14217        use some::mod1;
14218        use some::mod2;
14219
14220      - const A: u32 = 42;
14221      - const B: u32 = 42;
14222      - const C: u32 = 42;
14223        ˇ
14224
14225        fn main() {
14226            println!("hello");
14227
14228            println!("world");
14229        }
14230      "#
14231        .unindent(),
14232    );
14233
14234    cx.update_editor(|editor, window, cx| {
14235        editor.handle_input("replacement", window, cx);
14236    });
14237    executor.run_until_parked();
14238    cx.assert_state_with_diff(
14239        r#"
14240        use some::mod1;
14241        use some::mod2;
14242
14243      - const A: u32 = 42;
14244      - const B: u32 = 42;
14245      - const C: u32 = 42;
14246      -
14247      + replacementˇ
14248
14249        fn main() {
14250            println!("hello");
14251
14252            println!("world");
14253        }
14254      "#
14255        .unindent(),
14256    );
14257}
14258
14259#[gpui::test]
14260async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14261    init_test(cx, |_| {});
14262
14263    let mut cx = EditorTestContext::new(cx).await;
14264
14265    let base_text = r#"
14266        one
14267        two
14268        three
14269        four
14270        five
14271    "#
14272    .unindent();
14273    executor.run_until_parked();
14274    cx.set_state(
14275        &r#"
14276        one
14277        two
14278        fˇour
14279        five
14280        "#
14281        .unindent(),
14282    );
14283
14284    cx.set_head_text(&base_text);
14285    executor.run_until_parked();
14286
14287    cx.update_editor(|editor, window, cx| {
14288        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14289    });
14290    executor.run_until_parked();
14291
14292    cx.assert_state_with_diff(
14293        r#"
14294          one
14295          two
14296        - three
14297          fˇour
14298          five
14299        "#
14300        .unindent(),
14301    );
14302
14303    cx.update_editor(|editor, window, cx| {
14304        editor.backspace(&Backspace, window, cx);
14305        editor.backspace(&Backspace, window, cx);
14306    });
14307    executor.run_until_parked();
14308    cx.assert_state_with_diff(
14309        r#"
14310          one
14311          two
14312        - threeˇ
14313        - four
14314        + our
14315          five
14316        "#
14317        .unindent(),
14318    );
14319}
14320
14321#[gpui::test]
14322async fn test_edit_after_expanded_modification_hunk(
14323    executor: BackgroundExecutor,
14324    cx: &mut TestAppContext,
14325) {
14326    init_test(cx, |_| {});
14327
14328    let mut cx = EditorTestContext::new(cx).await;
14329
14330    let diff_base = r#"
14331        use some::mod1;
14332        use some::mod2;
14333
14334        const A: u32 = 42;
14335        const B: u32 = 42;
14336        const C: u32 = 42;
14337        const D: u32 = 42;
14338
14339
14340        fn main() {
14341            println!("hello");
14342
14343            println!("world");
14344        }"#
14345    .unindent();
14346
14347    cx.set_state(
14348        &r#"
14349        use some::mod1;
14350        use some::mod2;
14351
14352        const A: u32 = 42;
14353        const B: u32 = 42;
14354        const C: u32 = 43ˇ
14355        const D: u32 = 42;
14356
14357
14358        fn main() {
14359            println!("hello");
14360
14361            println!("world");
14362        }"#
14363        .unindent(),
14364    );
14365
14366    cx.set_head_text(&diff_base);
14367    executor.run_until_parked();
14368    cx.update_editor(|editor, window, cx| {
14369        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14370    });
14371    executor.run_until_parked();
14372
14373    cx.assert_state_with_diff(
14374        r#"
14375        use some::mod1;
14376        use some::mod2;
14377
14378        const A: u32 = 42;
14379        const B: u32 = 42;
14380      - const C: u32 = 42;
14381      + const C: u32 = 43ˇ
14382        const D: u32 = 42;
14383
14384
14385        fn main() {
14386            println!("hello");
14387
14388            println!("world");
14389        }"#
14390        .unindent(),
14391    );
14392
14393    cx.update_editor(|editor, window, cx| {
14394        editor.handle_input("\nnew_line\n", window, cx);
14395    });
14396    executor.run_until_parked();
14397
14398    cx.assert_state_with_diff(
14399        r#"
14400        use some::mod1;
14401        use some::mod2;
14402
14403        const A: u32 = 42;
14404        const B: u32 = 42;
14405      - const C: u32 = 42;
14406      + const C: u32 = 43
14407      + new_line
14408      + ˇ
14409        const D: u32 = 42;
14410
14411
14412        fn main() {
14413            println!("hello");
14414
14415            println!("world");
14416        }"#
14417        .unindent(),
14418    );
14419}
14420
14421#[gpui::test]
14422async fn test_stage_and_unstage_added_file_hunk(
14423    executor: BackgroundExecutor,
14424    cx: &mut TestAppContext,
14425) {
14426    init_test(cx, |_| {});
14427
14428    let mut cx = EditorTestContext::new(cx).await;
14429    cx.update_editor(|editor, _, cx| {
14430        editor.set_expand_all_diff_hunks(cx);
14431    });
14432
14433    let working_copy = r#"
14434            ˇfn main() {
14435                println!("hello, world!");
14436            }
14437        "#
14438    .unindent();
14439
14440    cx.set_state(&working_copy);
14441    executor.run_until_parked();
14442
14443    cx.assert_state_with_diff(
14444        r#"
14445            + ˇfn main() {
14446            +     println!("hello, world!");
14447            + }
14448        "#
14449        .unindent(),
14450    );
14451    cx.assert_index_text(None);
14452
14453    cx.update_editor(|editor, window, cx| {
14454        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14455    });
14456    executor.run_until_parked();
14457    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14458    cx.assert_state_with_diff(
14459        r#"
14460            + ˇfn main() {
14461            +     println!("hello, world!");
14462            + }
14463        "#
14464        .unindent(),
14465    );
14466
14467    cx.update_editor(|editor, window, cx| {
14468        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14469    });
14470    executor.run_until_parked();
14471    cx.assert_index_text(None);
14472}
14473
14474async fn setup_indent_guides_editor(
14475    text: &str,
14476    cx: &mut TestAppContext,
14477) -> (BufferId, EditorTestContext) {
14478    init_test(cx, |_| {});
14479
14480    let mut cx = EditorTestContext::new(cx).await;
14481
14482    let buffer_id = cx.update_editor(|editor, window, cx| {
14483        editor.set_text(text, window, cx);
14484        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14485
14486        buffer_ids[0]
14487    });
14488
14489    (buffer_id, cx)
14490}
14491
14492fn assert_indent_guides(
14493    range: Range<u32>,
14494    expected: Vec<IndentGuide>,
14495    active_indices: Option<Vec<usize>>,
14496    cx: &mut EditorTestContext,
14497) {
14498    let indent_guides = cx.update_editor(|editor, window, cx| {
14499        let snapshot = editor.snapshot(window, cx).display_snapshot;
14500        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14501            editor,
14502            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14503            true,
14504            &snapshot,
14505            cx,
14506        );
14507
14508        indent_guides.sort_by(|a, b| {
14509            a.depth.cmp(&b.depth).then(
14510                a.start_row
14511                    .cmp(&b.start_row)
14512                    .then(a.end_row.cmp(&b.end_row)),
14513            )
14514        });
14515        indent_guides
14516    });
14517
14518    if let Some(expected) = active_indices {
14519        let active_indices = cx.update_editor(|editor, window, cx| {
14520            let snapshot = editor.snapshot(window, cx).display_snapshot;
14521            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14522        });
14523
14524        assert_eq!(
14525            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14526            expected,
14527            "Active indent guide indices do not match"
14528        );
14529    }
14530
14531    assert_eq!(indent_guides, expected, "Indent guides do not match");
14532}
14533
14534fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14535    IndentGuide {
14536        buffer_id,
14537        start_row: MultiBufferRow(start_row),
14538        end_row: MultiBufferRow(end_row),
14539        depth,
14540        tab_size: 4,
14541        settings: IndentGuideSettings {
14542            enabled: true,
14543            line_width: 1,
14544            active_line_width: 1,
14545            ..Default::default()
14546        },
14547    }
14548}
14549
14550#[gpui::test]
14551async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14552    let (buffer_id, mut cx) = setup_indent_guides_editor(
14553        &"
14554    fn main() {
14555        let a = 1;
14556    }"
14557        .unindent(),
14558        cx,
14559    )
14560    .await;
14561
14562    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14563}
14564
14565#[gpui::test]
14566async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14567    let (buffer_id, mut cx) = setup_indent_guides_editor(
14568        &"
14569    fn main() {
14570        let a = 1;
14571        let b = 2;
14572    }"
14573        .unindent(),
14574        cx,
14575    )
14576    .await;
14577
14578    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14579}
14580
14581#[gpui::test]
14582async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14583    let (buffer_id, mut cx) = setup_indent_guides_editor(
14584        &"
14585    fn main() {
14586        let a = 1;
14587        if a == 3 {
14588            let b = 2;
14589        } else {
14590            let c = 3;
14591        }
14592    }"
14593        .unindent(),
14594        cx,
14595    )
14596    .await;
14597
14598    assert_indent_guides(
14599        0..8,
14600        vec![
14601            indent_guide(buffer_id, 1, 6, 0),
14602            indent_guide(buffer_id, 3, 3, 1),
14603            indent_guide(buffer_id, 5, 5, 1),
14604        ],
14605        None,
14606        &mut cx,
14607    );
14608}
14609
14610#[gpui::test]
14611async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14612    let (buffer_id, mut cx) = setup_indent_guides_editor(
14613        &"
14614    fn main() {
14615        let a = 1;
14616            let b = 2;
14617        let c = 3;
14618    }"
14619        .unindent(),
14620        cx,
14621    )
14622    .await;
14623
14624    assert_indent_guides(
14625        0..5,
14626        vec![
14627            indent_guide(buffer_id, 1, 3, 0),
14628            indent_guide(buffer_id, 2, 2, 1),
14629        ],
14630        None,
14631        &mut cx,
14632    );
14633}
14634
14635#[gpui::test]
14636async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14637    let (buffer_id, mut cx) = setup_indent_guides_editor(
14638        &"
14639        fn main() {
14640            let a = 1;
14641
14642            let c = 3;
14643        }"
14644        .unindent(),
14645        cx,
14646    )
14647    .await;
14648
14649    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14650}
14651
14652#[gpui::test]
14653async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14654    let (buffer_id, mut cx) = setup_indent_guides_editor(
14655        &"
14656        fn main() {
14657            let a = 1;
14658
14659            let c = 3;
14660
14661            if a == 3 {
14662                let b = 2;
14663            } else {
14664                let c = 3;
14665            }
14666        }"
14667        .unindent(),
14668        cx,
14669    )
14670    .await;
14671
14672    assert_indent_guides(
14673        0..11,
14674        vec![
14675            indent_guide(buffer_id, 1, 9, 0),
14676            indent_guide(buffer_id, 6, 6, 1),
14677            indent_guide(buffer_id, 8, 8, 1),
14678        ],
14679        None,
14680        &mut cx,
14681    );
14682}
14683
14684#[gpui::test]
14685async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14686    let (buffer_id, mut cx) = setup_indent_guides_editor(
14687        &"
14688        fn main() {
14689            let a = 1;
14690
14691            let c = 3;
14692
14693            if a == 3 {
14694                let b = 2;
14695            } else {
14696                let c = 3;
14697            }
14698        }"
14699        .unindent(),
14700        cx,
14701    )
14702    .await;
14703
14704    assert_indent_guides(
14705        1..11,
14706        vec![
14707            indent_guide(buffer_id, 1, 9, 0),
14708            indent_guide(buffer_id, 6, 6, 1),
14709            indent_guide(buffer_id, 8, 8, 1),
14710        ],
14711        None,
14712        &mut cx,
14713    );
14714}
14715
14716#[gpui::test]
14717async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14718    let (buffer_id, mut cx) = setup_indent_guides_editor(
14719        &"
14720        fn main() {
14721            let a = 1;
14722
14723            let c = 3;
14724
14725            if a == 3 {
14726                let b = 2;
14727            } else {
14728                let c = 3;
14729            }
14730        }"
14731        .unindent(),
14732        cx,
14733    )
14734    .await;
14735
14736    assert_indent_guides(
14737        1..10,
14738        vec![
14739            indent_guide(buffer_id, 1, 9, 0),
14740            indent_guide(buffer_id, 6, 6, 1),
14741            indent_guide(buffer_id, 8, 8, 1),
14742        ],
14743        None,
14744        &mut cx,
14745    );
14746}
14747
14748#[gpui::test]
14749async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14750    let (buffer_id, mut cx) = setup_indent_guides_editor(
14751        &"
14752        block1
14753            block2
14754                block3
14755                    block4
14756            block2
14757        block1
14758        block1"
14759            .unindent(),
14760        cx,
14761    )
14762    .await;
14763
14764    assert_indent_guides(
14765        1..10,
14766        vec![
14767            indent_guide(buffer_id, 1, 4, 0),
14768            indent_guide(buffer_id, 2, 3, 1),
14769            indent_guide(buffer_id, 3, 3, 2),
14770        ],
14771        None,
14772        &mut cx,
14773    );
14774}
14775
14776#[gpui::test]
14777async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14778    let (buffer_id, mut cx) = setup_indent_guides_editor(
14779        &"
14780        block1
14781            block2
14782                block3
14783
14784        block1
14785        block1"
14786            .unindent(),
14787        cx,
14788    )
14789    .await;
14790
14791    assert_indent_guides(
14792        0..6,
14793        vec![
14794            indent_guide(buffer_id, 1, 2, 0),
14795            indent_guide(buffer_id, 2, 2, 1),
14796        ],
14797        None,
14798        &mut cx,
14799    );
14800}
14801
14802#[gpui::test]
14803async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14804    let (buffer_id, mut cx) = setup_indent_guides_editor(
14805        &"
14806        block1
14807
14808
14809
14810            block2
14811        "
14812        .unindent(),
14813        cx,
14814    )
14815    .await;
14816
14817    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14818}
14819
14820#[gpui::test]
14821async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14822    let (buffer_id, mut cx) = setup_indent_guides_editor(
14823        &"
14824        def a:
14825        \tb = 3
14826        \tif True:
14827        \t\tc = 4
14828        \t\td = 5
14829        \tprint(b)
14830        "
14831        .unindent(),
14832        cx,
14833    )
14834    .await;
14835
14836    assert_indent_guides(
14837        0..6,
14838        vec![
14839            indent_guide(buffer_id, 1, 6, 0),
14840            indent_guide(buffer_id, 3, 4, 1),
14841        ],
14842        None,
14843        &mut cx,
14844    );
14845}
14846
14847#[gpui::test]
14848async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14849    let (buffer_id, mut cx) = setup_indent_guides_editor(
14850        &"
14851    fn main() {
14852        let a = 1;
14853    }"
14854        .unindent(),
14855        cx,
14856    )
14857    .await;
14858
14859    cx.update_editor(|editor, window, cx| {
14860        editor.change_selections(None, window, cx, |s| {
14861            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14862        });
14863    });
14864
14865    assert_indent_guides(
14866        0..3,
14867        vec![indent_guide(buffer_id, 1, 1, 0)],
14868        Some(vec![0]),
14869        &mut cx,
14870    );
14871}
14872
14873#[gpui::test]
14874async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14875    let (buffer_id, mut cx) = setup_indent_guides_editor(
14876        &"
14877    fn main() {
14878        if 1 == 2 {
14879            let a = 1;
14880        }
14881    }"
14882        .unindent(),
14883        cx,
14884    )
14885    .await;
14886
14887    cx.update_editor(|editor, window, cx| {
14888        editor.change_selections(None, window, cx, |s| {
14889            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14890        });
14891    });
14892
14893    assert_indent_guides(
14894        0..4,
14895        vec![
14896            indent_guide(buffer_id, 1, 3, 0),
14897            indent_guide(buffer_id, 2, 2, 1),
14898        ],
14899        Some(vec![1]),
14900        &mut cx,
14901    );
14902
14903    cx.update_editor(|editor, window, cx| {
14904        editor.change_selections(None, window, cx, |s| {
14905            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14906        });
14907    });
14908
14909    assert_indent_guides(
14910        0..4,
14911        vec![
14912            indent_guide(buffer_id, 1, 3, 0),
14913            indent_guide(buffer_id, 2, 2, 1),
14914        ],
14915        Some(vec![1]),
14916        &mut cx,
14917    );
14918
14919    cx.update_editor(|editor, window, cx| {
14920        editor.change_selections(None, window, cx, |s| {
14921            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14922        });
14923    });
14924
14925    assert_indent_guides(
14926        0..4,
14927        vec![
14928            indent_guide(buffer_id, 1, 3, 0),
14929            indent_guide(buffer_id, 2, 2, 1),
14930        ],
14931        Some(vec![0]),
14932        &mut cx,
14933    );
14934}
14935
14936#[gpui::test]
14937async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
14938    let (buffer_id, mut cx) = setup_indent_guides_editor(
14939        &"
14940    fn main() {
14941        let a = 1;
14942
14943        let b = 2;
14944    }"
14945        .unindent(),
14946        cx,
14947    )
14948    .await;
14949
14950    cx.update_editor(|editor, window, cx| {
14951        editor.change_selections(None, window, cx, |s| {
14952            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14953        });
14954    });
14955
14956    assert_indent_guides(
14957        0..5,
14958        vec![indent_guide(buffer_id, 1, 3, 0)],
14959        Some(vec![0]),
14960        &mut cx,
14961    );
14962}
14963
14964#[gpui::test]
14965async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
14966    let (buffer_id, mut cx) = setup_indent_guides_editor(
14967        &"
14968    def m:
14969        a = 1
14970        pass"
14971            .unindent(),
14972        cx,
14973    )
14974    .await;
14975
14976    cx.update_editor(|editor, window, cx| {
14977        editor.change_selections(None, window, cx, |s| {
14978            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14979        });
14980    });
14981
14982    assert_indent_guides(
14983        0..3,
14984        vec![indent_guide(buffer_id, 1, 2, 0)],
14985        Some(vec![0]),
14986        &mut cx,
14987    );
14988}
14989
14990#[gpui::test]
14991async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
14992    init_test(cx, |_| {});
14993    let mut cx = EditorTestContext::new(cx).await;
14994    let text = indoc! {
14995        "
14996        impl A {
14997            fn b() {
14998                0;
14999                3;
15000                5;
15001                6;
15002                7;
15003            }
15004        }
15005        "
15006    };
15007    let base_text = indoc! {
15008        "
15009        impl A {
15010            fn b() {
15011                0;
15012                1;
15013                2;
15014                3;
15015                4;
15016            }
15017            fn c() {
15018                5;
15019                6;
15020                7;
15021            }
15022        }
15023        "
15024    };
15025
15026    cx.update_editor(|editor, window, cx| {
15027        editor.set_text(text, window, cx);
15028
15029        editor.buffer().update(cx, |multibuffer, cx| {
15030            let buffer = multibuffer.as_singleton().unwrap();
15031            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15032
15033            multibuffer.set_all_diff_hunks_expanded(cx);
15034            multibuffer.add_diff(diff, cx);
15035
15036            buffer.read(cx).remote_id()
15037        })
15038    });
15039    cx.run_until_parked();
15040
15041    cx.assert_state_with_diff(
15042        indoc! { "
15043          impl A {
15044              fn b() {
15045                  0;
15046        -         1;
15047        -         2;
15048                  3;
15049        -         4;
15050        -     }
15051        -     fn c() {
15052                  5;
15053                  6;
15054                  7;
15055              }
15056          }
15057          ˇ"
15058        }
15059        .to_string(),
15060    );
15061
15062    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15063        editor
15064            .snapshot(window, cx)
15065            .buffer_snapshot
15066            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15067            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15068            .collect::<Vec<_>>()
15069    });
15070    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15071    assert_eq!(
15072        actual_guides,
15073        vec![
15074            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15075            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15076            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15077        ]
15078    );
15079}
15080
15081#[gpui::test]
15082async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15083    init_test(cx, |_| {});
15084    let mut cx = EditorTestContext::new(cx).await;
15085
15086    let diff_base = r#"
15087        a
15088        b
15089        c
15090        "#
15091    .unindent();
15092
15093    cx.set_state(
15094        &r#"
15095        ˇA
15096        b
15097        C
15098        "#
15099        .unindent(),
15100    );
15101    cx.set_head_text(&diff_base);
15102    cx.update_editor(|editor, window, cx| {
15103        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15104    });
15105    executor.run_until_parked();
15106
15107    let both_hunks_expanded = r#"
15108        - a
15109        + ˇA
15110          b
15111        - c
15112        + C
15113        "#
15114    .unindent();
15115
15116    cx.assert_state_with_diff(both_hunks_expanded.clone());
15117
15118    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15119        let snapshot = editor.snapshot(window, cx);
15120        let hunks = editor
15121            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15122            .collect::<Vec<_>>();
15123        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15124        let buffer_id = hunks[0].buffer_id;
15125        hunks
15126            .into_iter()
15127            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15128            .collect::<Vec<_>>()
15129    });
15130    assert_eq!(hunk_ranges.len(), 2);
15131
15132    cx.update_editor(|editor, _, cx| {
15133        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15134    });
15135    executor.run_until_parked();
15136
15137    let second_hunk_expanded = r#"
15138          ˇA
15139          b
15140        - c
15141        + C
15142        "#
15143    .unindent();
15144
15145    cx.assert_state_with_diff(second_hunk_expanded);
15146
15147    cx.update_editor(|editor, _, cx| {
15148        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15149    });
15150    executor.run_until_parked();
15151
15152    cx.assert_state_with_diff(both_hunks_expanded.clone());
15153
15154    cx.update_editor(|editor, _, cx| {
15155        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15156    });
15157    executor.run_until_parked();
15158
15159    let first_hunk_expanded = r#"
15160        - a
15161        + ˇA
15162          b
15163          C
15164        "#
15165    .unindent();
15166
15167    cx.assert_state_with_diff(first_hunk_expanded);
15168
15169    cx.update_editor(|editor, _, cx| {
15170        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15171    });
15172    executor.run_until_parked();
15173
15174    cx.assert_state_with_diff(both_hunks_expanded);
15175
15176    cx.set_state(
15177        &r#"
15178        ˇA
15179        b
15180        "#
15181        .unindent(),
15182    );
15183    cx.run_until_parked();
15184
15185    // TODO this cursor position seems bad
15186    cx.assert_state_with_diff(
15187        r#"
15188        - ˇa
15189        + A
15190          b
15191        "#
15192        .unindent(),
15193    );
15194
15195    cx.update_editor(|editor, window, cx| {
15196        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15197    });
15198
15199    cx.assert_state_with_diff(
15200        r#"
15201            - ˇa
15202            + A
15203              b
15204            - c
15205            "#
15206        .unindent(),
15207    );
15208
15209    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15210        let snapshot = editor.snapshot(window, cx);
15211        let hunks = editor
15212            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15213            .collect::<Vec<_>>();
15214        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15215        let buffer_id = hunks[0].buffer_id;
15216        hunks
15217            .into_iter()
15218            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15219            .collect::<Vec<_>>()
15220    });
15221    assert_eq!(hunk_ranges.len(), 2);
15222
15223    cx.update_editor(|editor, _, cx| {
15224        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15225    });
15226    executor.run_until_parked();
15227
15228    cx.assert_state_with_diff(
15229        r#"
15230        - ˇa
15231        + A
15232          b
15233        "#
15234        .unindent(),
15235    );
15236}
15237
15238#[gpui::test]
15239async fn test_toggle_deletion_hunk_at_start_of_file(
15240    executor: BackgroundExecutor,
15241    cx: &mut TestAppContext,
15242) {
15243    init_test(cx, |_| {});
15244    let mut cx = EditorTestContext::new(cx).await;
15245
15246    let diff_base = r#"
15247        a
15248        b
15249        c
15250        "#
15251    .unindent();
15252
15253    cx.set_state(
15254        &r#"
15255        ˇb
15256        c
15257        "#
15258        .unindent(),
15259    );
15260    cx.set_head_text(&diff_base);
15261    cx.update_editor(|editor, window, cx| {
15262        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15263    });
15264    executor.run_until_parked();
15265
15266    let hunk_expanded = r#"
15267        - a
15268          ˇb
15269          c
15270        "#
15271    .unindent();
15272
15273    cx.assert_state_with_diff(hunk_expanded.clone());
15274
15275    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15276        let snapshot = editor.snapshot(window, cx);
15277        let hunks = editor
15278            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15279            .collect::<Vec<_>>();
15280        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15281        let buffer_id = hunks[0].buffer_id;
15282        hunks
15283            .into_iter()
15284            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15285            .collect::<Vec<_>>()
15286    });
15287    assert_eq!(hunk_ranges.len(), 1);
15288
15289    cx.update_editor(|editor, _, cx| {
15290        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15291    });
15292    executor.run_until_parked();
15293
15294    let hunk_collapsed = r#"
15295          ˇb
15296          c
15297        "#
15298    .unindent();
15299
15300    cx.assert_state_with_diff(hunk_collapsed);
15301
15302    cx.update_editor(|editor, _, cx| {
15303        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15304    });
15305    executor.run_until_parked();
15306
15307    cx.assert_state_with_diff(hunk_expanded.clone());
15308}
15309
15310#[gpui::test]
15311async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15312    init_test(cx, |_| {});
15313
15314    let fs = FakeFs::new(cx.executor());
15315    fs.insert_tree(
15316        path!("/test"),
15317        json!({
15318            ".git": {},
15319            "file-1": "ONE\n",
15320            "file-2": "TWO\n",
15321            "file-3": "THREE\n",
15322        }),
15323    )
15324    .await;
15325
15326    fs.set_head_for_repo(
15327        path!("/test/.git").as_ref(),
15328        &[
15329            ("file-1".into(), "one\n".into()),
15330            ("file-2".into(), "two\n".into()),
15331            ("file-3".into(), "three\n".into()),
15332        ],
15333    );
15334
15335    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15336    let mut buffers = vec![];
15337    for i in 1..=3 {
15338        let buffer = project
15339            .update(cx, |project, cx| {
15340                let path = format!(path!("/test/file-{}"), i);
15341                project.open_local_buffer(path, cx)
15342            })
15343            .await
15344            .unwrap();
15345        buffers.push(buffer);
15346    }
15347
15348    let multibuffer = cx.new(|cx| {
15349        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15350        multibuffer.set_all_diff_hunks_expanded(cx);
15351        for buffer in &buffers {
15352            let snapshot = buffer.read(cx).snapshot();
15353            multibuffer.set_excerpts_for_path(
15354                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15355                buffer.clone(),
15356                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15357                DEFAULT_MULTIBUFFER_CONTEXT,
15358                cx,
15359            );
15360        }
15361        multibuffer
15362    });
15363
15364    let editor = cx.add_window(|window, cx| {
15365        Editor::new(
15366            EditorMode::Full,
15367            multibuffer,
15368            Some(project),
15369            true,
15370            window,
15371            cx,
15372        )
15373    });
15374    cx.run_until_parked();
15375
15376    let snapshot = editor
15377        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15378        .unwrap();
15379    let hunks = snapshot
15380        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15381        .map(|hunk| match hunk {
15382            DisplayDiffHunk::Unfolded {
15383                display_row_range, ..
15384            } => display_row_range,
15385            DisplayDiffHunk::Folded { .. } => unreachable!(),
15386        })
15387        .collect::<Vec<_>>();
15388    assert_eq!(
15389        hunks,
15390        [
15391            DisplayRow(3)..DisplayRow(5),
15392            DisplayRow(10)..DisplayRow(12),
15393            DisplayRow(17)..DisplayRow(19),
15394        ]
15395    );
15396}
15397
15398#[gpui::test]
15399async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15400    init_test(cx, |_| {});
15401
15402    let mut cx = EditorTestContext::new(cx).await;
15403    cx.set_head_text(indoc! { "
15404        one
15405        two
15406        three
15407        four
15408        five
15409        "
15410    });
15411    cx.set_index_text(indoc! { "
15412        one
15413        two
15414        three
15415        four
15416        five
15417        "
15418    });
15419    cx.set_state(indoc! {"
15420        one
15421        TWO
15422        ˇTHREE
15423        FOUR
15424        five
15425    "});
15426    cx.run_until_parked();
15427    cx.update_editor(|editor, window, cx| {
15428        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15429    });
15430    cx.run_until_parked();
15431    cx.assert_index_text(Some(indoc! {"
15432        one
15433        TWO
15434        THREE
15435        FOUR
15436        five
15437    "}));
15438    cx.set_state(indoc! { "
15439        one
15440        TWO
15441        ˇTHREE-HUNDRED
15442        FOUR
15443        five
15444    "});
15445    cx.run_until_parked();
15446    cx.update_editor(|editor, window, cx| {
15447        let snapshot = editor.snapshot(window, cx);
15448        let hunks = editor
15449            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15450            .collect::<Vec<_>>();
15451        assert_eq!(hunks.len(), 1);
15452        assert_eq!(
15453            hunks[0].status(),
15454            DiffHunkStatus {
15455                kind: DiffHunkStatusKind::Modified,
15456                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15457            }
15458        );
15459
15460        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15461    });
15462    cx.run_until_parked();
15463    cx.assert_index_text(Some(indoc! {"
15464        one
15465        TWO
15466        THREE-HUNDRED
15467        FOUR
15468        five
15469    "}));
15470}
15471
15472#[gpui::test]
15473fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15474    init_test(cx, |_| {});
15475
15476    let editor = cx.add_window(|window, cx| {
15477        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15478        build_editor(buffer, window, cx)
15479    });
15480
15481    let render_args = Arc::new(Mutex::new(None));
15482    let snapshot = editor
15483        .update(cx, |editor, window, cx| {
15484            let snapshot = editor.buffer().read(cx).snapshot(cx);
15485            let range =
15486                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15487
15488            struct RenderArgs {
15489                row: MultiBufferRow,
15490                folded: bool,
15491                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15492            }
15493
15494            let crease = Crease::inline(
15495                range,
15496                FoldPlaceholder::test(),
15497                {
15498                    let toggle_callback = render_args.clone();
15499                    move |row, folded, callback, _window, _cx| {
15500                        *toggle_callback.lock() = Some(RenderArgs {
15501                            row,
15502                            folded,
15503                            callback,
15504                        });
15505                        div()
15506                    }
15507                },
15508                |_row, _folded, _window, _cx| div(),
15509            );
15510
15511            editor.insert_creases(Some(crease), cx);
15512            let snapshot = editor.snapshot(window, cx);
15513            let _div = snapshot.render_crease_toggle(
15514                MultiBufferRow(1),
15515                false,
15516                cx.entity().clone(),
15517                window,
15518                cx,
15519            );
15520            snapshot
15521        })
15522        .unwrap();
15523
15524    let render_args = render_args.lock().take().unwrap();
15525    assert_eq!(render_args.row, MultiBufferRow(1));
15526    assert!(!render_args.folded);
15527    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15528
15529    cx.update_window(*editor, |_, window, cx| {
15530        (render_args.callback)(true, window, cx)
15531    })
15532    .unwrap();
15533    let snapshot = editor
15534        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15535        .unwrap();
15536    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15537
15538    cx.update_window(*editor, |_, window, cx| {
15539        (render_args.callback)(false, window, cx)
15540    })
15541    .unwrap();
15542    let snapshot = editor
15543        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15544        .unwrap();
15545    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15546}
15547
15548#[gpui::test]
15549async fn test_input_text(cx: &mut TestAppContext) {
15550    init_test(cx, |_| {});
15551    let mut cx = EditorTestContext::new(cx).await;
15552
15553    cx.set_state(
15554        &r#"ˇone
15555        two
15556
15557        three
15558        fourˇ
15559        five
15560
15561        siˇx"#
15562            .unindent(),
15563    );
15564
15565    cx.dispatch_action(HandleInput(String::new()));
15566    cx.assert_editor_state(
15567        &r#"ˇone
15568        two
15569
15570        three
15571        fourˇ
15572        five
15573
15574        siˇx"#
15575            .unindent(),
15576    );
15577
15578    cx.dispatch_action(HandleInput("AAAA".to_string()));
15579    cx.assert_editor_state(
15580        &r#"AAAAˇone
15581        two
15582
15583        three
15584        fourAAAAˇ
15585        five
15586
15587        siAAAAˇx"#
15588            .unindent(),
15589    );
15590}
15591
15592#[gpui::test]
15593async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15594    init_test(cx, |_| {});
15595
15596    let mut cx = EditorTestContext::new(cx).await;
15597    cx.set_state(
15598        r#"let foo = 1;
15599let foo = 2;
15600let foo = 3;
15601let fooˇ = 4;
15602let foo = 5;
15603let foo = 6;
15604let foo = 7;
15605let foo = 8;
15606let foo = 9;
15607let foo = 10;
15608let foo = 11;
15609let foo = 12;
15610let foo = 13;
15611let foo = 14;
15612let foo = 15;"#,
15613    );
15614
15615    cx.update_editor(|e, window, cx| {
15616        assert_eq!(
15617            e.next_scroll_position,
15618            NextScrollCursorCenterTopBottom::Center,
15619            "Default next scroll direction is center",
15620        );
15621
15622        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15623        assert_eq!(
15624            e.next_scroll_position,
15625            NextScrollCursorCenterTopBottom::Top,
15626            "After center, next scroll direction should be top",
15627        );
15628
15629        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15630        assert_eq!(
15631            e.next_scroll_position,
15632            NextScrollCursorCenterTopBottom::Bottom,
15633            "After top, next scroll direction should be bottom",
15634        );
15635
15636        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15637        assert_eq!(
15638            e.next_scroll_position,
15639            NextScrollCursorCenterTopBottom::Center,
15640            "After bottom, scrolling should start over",
15641        );
15642
15643        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15644        assert_eq!(
15645            e.next_scroll_position,
15646            NextScrollCursorCenterTopBottom::Top,
15647            "Scrolling continues if retriggered fast enough"
15648        );
15649    });
15650
15651    cx.executor()
15652        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15653    cx.executor().run_until_parked();
15654    cx.update_editor(|e, _, _| {
15655        assert_eq!(
15656            e.next_scroll_position,
15657            NextScrollCursorCenterTopBottom::Center,
15658            "If scrolling is not triggered fast enough, it should reset"
15659        );
15660    });
15661}
15662
15663#[gpui::test]
15664async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15665    init_test(cx, |_| {});
15666    let mut cx = EditorLspTestContext::new_rust(
15667        lsp::ServerCapabilities {
15668            definition_provider: Some(lsp::OneOf::Left(true)),
15669            references_provider: Some(lsp::OneOf::Left(true)),
15670            ..lsp::ServerCapabilities::default()
15671        },
15672        cx,
15673    )
15674    .await;
15675
15676    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15677        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15678            move |params, _| async move {
15679                if empty_go_to_definition {
15680                    Ok(None)
15681                } else {
15682                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15683                        uri: params.text_document_position_params.text_document.uri,
15684                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15685                    })))
15686                }
15687            },
15688        );
15689        let references =
15690            cx.lsp
15691                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15692                    Ok(Some(vec![lsp::Location {
15693                        uri: params.text_document_position.text_document.uri,
15694                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15695                    }]))
15696                });
15697        (go_to_definition, references)
15698    };
15699
15700    cx.set_state(
15701        &r#"fn one() {
15702            let mut a = ˇtwo();
15703        }
15704
15705        fn two() {}"#
15706            .unindent(),
15707    );
15708    set_up_lsp_handlers(false, &mut cx);
15709    let navigated = cx
15710        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15711        .await
15712        .expect("Failed to navigate to definition");
15713    assert_eq!(
15714        navigated,
15715        Navigated::Yes,
15716        "Should have navigated to definition from the GetDefinition response"
15717    );
15718    cx.assert_editor_state(
15719        &r#"fn one() {
15720            let mut a = two();
15721        }
15722
15723        fn «twoˇ»() {}"#
15724            .unindent(),
15725    );
15726
15727    let editors = cx.update_workspace(|workspace, _, cx| {
15728        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15729    });
15730    cx.update_editor(|_, _, test_editor_cx| {
15731        assert_eq!(
15732            editors.len(),
15733            1,
15734            "Initially, only one, test, editor should be open in the workspace"
15735        );
15736        assert_eq!(
15737            test_editor_cx.entity(),
15738            editors.last().expect("Asserted len is 1").clone()
15739        );
15740    });
15741
15742    set_up_lsp_handlers(true, &mut cx);
15743    let navigated = cx
15744        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15745        .await
15746        .expect("Failed to navigate to lookup references");
15747    assert_eq!(
15748        navigated,
15749        Navigated::Yes,
15750        "Should have navigated to references as a fallback after empty GoToDefinition response"
15751    );
15752    // We should not change the selections in the existing file,
15753    // if opening another milti buffer with the references
15754    cx.assert_editor_state(
15755        &r#"fn one() {
15756            let mut a = two();
15757        }
15758
15759        fn «twoˇ»() {}"#
15760            .unindent(),
15761    );
15762    let editors = cx.update_workspace(|workspace, _, cx| {
15763        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15764    });
15765    cx.update_editor(|_, _, test_editor_cx| {
15766        assert_eq!(
15767            editors.len(),
15768            2,
15769            "After falling back to references search, we open a new editor with the results"
15770        );
15771        let references_fallback_text = editors
15772            .into_iter()
15773            .find(|new_editor| *new_editor != test_editor_cx.entity())
15774            .expect("Should have one non-test editor now")
15775            .read(test_editor_cx)
15776            .text(test_editor_cx);
15777        assert_eq!(
15778            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15779            "Should use the range from the references response and not the GoToDefinition one"
15780        );
15781    });
15782}
15783
15784#[gpui::test]
15785async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15786    init_test(cx, |_| {});
15787
15788    let language = Arc::new(Language::new(
15789        LanguageConfig::default(),
15790        Some(tree_sitter_rust::LANGUAGE.into()),
15791    ));
15792
15793    let text = r#"
15794        #[cfg(test)]
15795        mod tests() {
15796            #[test]
15797            fn runnable_1() {
15798                let a = 1;
15799            }
15800
15801            #[test]
15802            fn runnable_2() {
15803                let a = 1;
15804                let b = 2;
15805            }
15806        }
15807    "#
15808    .unindent();
15809
15810    let fs = FakeFs::new(cx.executor());
15811    fs.insert_file("/file.rs", Default::default()).await;
15812
15813    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15814    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15815    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15816    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15817    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15818
15819    let editor = cx.new_window_entity(|window, cx| {
15820        Editor::new(
15821            EditorMode::Full,
15822            multi_buffer,
15823            Some(project.clone()),
15824            true,
15825            window,
15826            cx,
15827        )
15828    });
15829
15830    editor.update_in(cx, |editor, window, cx| {
15831        let snapshot = editor.buffer().read(cx).snapshot(cx);
15832        editor.tasks.insert(
15833            (buffer.read(cx).remote_id(), 3),
15834            RunnableTasks {
15835                templates: vec![],
15836                offset: snapshot.anchor_before(43),
15837                column: 0,
15838                extra_variables: HashMap::default(),
15839                context_range: BufferOffset(43)..BufferOffset(85),
15840            },
15841        );
15842        editor.tasks.insert(
15843            (buffer.read(cx).remote_id(), 8),
15844            RunnableTasks {
15845                templates: vec![],
15846                offset: snapshot.anchor_before(86),
15847                column: 0,
15848                extra_variables: HashMap::default(),
15849                context_range: BufferOffset(86)..BufferOffset(191),
15850            },
15851        );
15852
15853        // Test finding task when cursor is inside function body
15854        editor.change_selections(None, window, cx, |s| {
15855            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15856        });
15857        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15858        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15859
15860        // Test finding task when cursor is on function name
15861        editor.change_selections(None, window, cx, |s| {
15862            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15863        });
15864        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15865        assert_eq!(row, 8, "Should find task when cursor is on function name");
15866    });
15867}
15868
15869#[gpui::test]
15870async fn test_folding_buffers(cx: &mut TestAppContext) {
15871    init_test(cx, |_| {});
15872
15873    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15874    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15875    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15876
15877    let fs = FakeFs::new(cx.executor());
15878    fs.insert_tree(
15879        path!("/a"),
15880        json!({
15881            "first.rs": sample_text_1,
15882            "second.rs": sample_text_2,
15883            "third.rs": sample_text_3,
15884        }),
15885    )
15886    .await;
15887    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15888    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15889    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15890    let worktree = project.update(cx, |project, cx| {
15891        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15892        assert_eq!(worktrees.len(), 1);
15893        worktrees.pop().unwrap()
15894    });
15895    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15896
15897    let buffer_1 = project
15898        .update(cx, |project, cx| {
15899            project.open_buffer((worktree_id, "first.rs"), cx)
15900        })
15901        .await
15902        .unwrap();
15903    let buffer_2 = project
15904        .update(cx, |project, cx| {
15905            project.open_buffer((worktree_id, "second.rs"), cx)
15906        })
15907        .await
15908        .unwrap();
15909    let buffer_3 = project
15910        .update(cx, |project, cx| {
15911            project.open_buffer((worktree_id, "third.rs"), cx)
15912        })
15913        .await
15914        .unwrap();
15915
15916    let multi_buffer = cx.new(|cx| {
15917        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15918        multi_buffer.push_excerpts(
15919            buffer_1.clone(),
15920            [
15921                ExcerptRange {
15922                    context: Point::new(0, 0)..Point::new(3, 0),
15923                    primary: None,
15924                },
15925                ExcerptRange {
15926                    context: Point::new(5, 0)..Point::new(7, 0),
15927                    primary: None,
15928                },
15929                ExcerptRange {
15930                    context: Point::new(9, 0)..Point::new(10, 4),
15931                    primary: None,
15932                },
15933            ],
15934            cx,
15935        );
15936        multi_buffer.push_excerpts(
15937            buffer_2.clone(),
15938            [
15939                ExcerptRange {
15940                    context: Point::new(0, 0)..Point::new(3, 0),
15941                    primary: None,
15942                },
15943                ExcerptRange {
15944                    context: Point::new(5, 0)..Point::new(7, 0),
15945                    primary: None,
15946                },
15947                ExcerptRange {
15948                    context: Point::new(9, 0)..Point::new(10, 4),
15949                    primary: None,
15950                },
15951            ],
15952            cx,
15953        );
15954        multi_buffer.push_excerpts(
15955            buffer_3.clone(),
15956            [
15957                ExcerptRange {
15958                    context: Point::new(0, 0)..Point::new(3, 0),
15959                    primary: None,
15960                },
15961                ExcerptRange {
15962                    context: Point::new(5, 0)..Point::new(7, 0),
15963                    primary: None,
15964                },
15965                ExcerptRange {
15966                    context: Point::new(9, 0)..Point::new(10, 4),
15967                    primary: None,
15968                },
15969            ],
15970            cx,
15971        );
15972        multi_buffer
15973    });
15974    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15975        Editor::new(
15976            EditorMode::Full,
15977            multi_buffer.clone(),
15978            Some(project.clone()),
15979            true,
15980            window,
15981            cx,
15982        )
15983    });
15984
15985    assert_eq!(
15986        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15987        "\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",
15988    );
15989
15990    multi_buffer_editor.update(cx, |editor, cx| {
15991        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15992    });
15993    assert_eq!(
15994        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15995        "\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",
15996        "After folding the first buffer, its text should not be displayed"
15997    );
15998
15999    multi_buffer_editor.update(cx, |editor, cx| {
16000        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16001    });
16002    assert_eq!(
16003        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16004        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
16005        "After folding the second buffer, its text should not be displayed"
16006    );
16007
16008    multi_buffer_editor.update(cx, |editor, cx| {
16009        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16010    });
16011    assert_eq!(
16012        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16013        "\n\n\n\n\n",
16014        "After folding the third buffer, its text should not be displayed"
16015    );
16016
16017    // Emulate selection inside the fold logic, that should work
16018    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16019        editor
16020            .snapshot(window, cx)
16021            .next_line_boundary(Point::new(0, 4));
16022    });
16023
16024    multi_buffer_editor.update(cx, |editor, cx| {
16025        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16026    });
16027    assert_eq!(
16028        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16029        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
16030        "After unfolding the second buffer, its text should be displayed"
16031    );
16032
16033    // Typing inside of buffer 1 causes that buffer to be unfolded.
16034    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16035        assert_eq!(
16036            multi_buffer
16037                .read(cx)
16038                .snapshot(cx)
16039                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16040                .collect::<String>(),
16041            "bbbb"
16042        );
16043        editor.change_selections(None, window, cx, |selections| {
16044            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16045        });
16046        editor.handle_input("B", window, cx);
16047    });
16048
16049    assert_eq!(
16050        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16051        "\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",
16052        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16053    );
16054
16055    multi_buffer_editor.update(cx, |editor, cx| {
16056        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16057    });
16058    assert_eq!(
16059        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16060        "\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",
16061        "After unfolding the all buffers, all original text should be displayed"
16062    );
16063}
16064
16065#[gpui::test]
16066async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16067    init_test(cx, |_| {});
16068
16069    let sample_text_1 = "1111\n2222\n3333".to_string();
16070    let sample_text_2 = "4444\n5555\n6666".to_string();
16071    let sample_text_3 = "7777\n8888\n9999".to_string();
16072
16073    let fs = FakeFs::new(cx.executor());
16074    fs.insert_tree(
16075        path!("/a"),
16076        json!({
16077            "first.rs": sample_text_1,
16078            "second.rs": sample_text_2,
16079            "third.rs": sample_text_3,
16080        }),
16081    )
16082    .await;
16083    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16084    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16085    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16086    let worktree = project.update(cx, |project, cx| {
16087        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16088        assert_eq!(worktrees.len(), 1);
16089        worktrees.pop().unwrap()
16090    });
16091    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16092
16093    let buffer_1 = project
16094        .update(cx, |project, cx| {
16095            project.open_buffer((worktree_id, "first.rs"), cx)
16096        })
16097        .await
16098        .unwrap();
16099    let buffer_2 = project
16100        .update(cx, |project, cx| {
16101            project.open_buffer((worktree_id, "second.rs"), cx)
16102        })
16103        .await
16104        .unwrap();
16105    let buffer_3 = project
16106        .update(cx, |project, cx| {
16107            project.open_buffer((worktree_id, "third.rs"), cx)
16108        })
16109        .await
16110        .unwrap();
16111
16112    let multi_buffer = cx.new(|cx| {
16113        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16114        multi_buffer.push_excerpts(
16115            buffer_1.clone(),
16116            [ExcerptRange {
16117                context: Point::new(0, 0)..Point::new(3, 0),
16118                primary: None,
16119            }],
16120            cx,
16121        );
16122        multi_buffer.push_excerpts(
16123            buffer_2.clone(),
16124            [ExcerptRange {
16125                context: Point::new(0, 0)..Point::new(3, 0),
16126                primary: None,
16127            }],
16128            cx,
16129        );
16130        multi_buffer.push_excerpts(
16131            buffer_3.clone(),
16132            [ExcerptRange {
16133                context: Point::new(0, 0)..Point::new(3, 0),
16134                primary: None,
16135            }],
16136            cx,
16137        );
16138        multi_buffer
16139    });
16140
16141    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16142        Editor::new(
16143            EditorMode::Full,
16144            multi_buffer,
16145            Some(project.clone()),
16146            true,
16147            window,
16148            cx,
16149        )
16150    });
16151
16152    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
16153    assert_eq!(
16154        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16155        full_text,
16156    );
16157
16158    multi_buffer_editor.update(cx, |editor, cx| {
16159        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16160    });
16161    assert_eq!(
16162        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16163        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
16164        "After folding the first buffer, its text should not be displayed"
16165    );
16166
16167    multi_buffer_editor.update(cx, |editor, cx| {
16168        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16169    });
16170
16171    assert_eq!(
16172        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16173        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
16174        "After folding the second buffer, its text should not be displayed"
16175    );
16176
16177    multi_buffer_editor.update(cx, |editor, cx| {
16178        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16179    });
16180    assert_eq!(
16181        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16182        "\n\n\n\n\n",
16183        "After folding the third buffer, its text should not be displayed"
16184    );
16185
16186    multi_buffer_editor.update(cx, |editor, cx| {
16187        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16188    });
16189    assert_eq!(
16190        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16191        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
16192        "After unfolding the second buffer, its text should be displayed"
16193    );
16194
16195    multi_buffer_editor.update(cx, |editor, cx| {
16196        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16197    });
16198    assert_eq!(
16199        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16200        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
16201        "After unfolding the first buffer, its text should be displayed"
16202    );
16203
16204    multi_buffer_editor.update(cx, |editor, cx| {
16205        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16206    });
16207    assert_eq!(
16208        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16209        full_text,
16210        "After unfolding all buffers, all original text should be displayed"
16211    );
16212}
16213
16214#[gpui::test]
16215async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16216    init_test(cx, |_| {});
16217
16218    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16219
16220    let fs = FakeFs::new(cx.executor());
16221    fs.insert_tree(
16222        path!("/a"),
16223        json!({
16224            "main.rs": sample_text,
16225        }),
16226    )
16227    .await;
16228    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16229    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16230    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16231    let worktree = project.update(cx, |project, cx| {
16232        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16233        assert_eq!(worktrees.len(), 1);
16234        worktrees.pop().unwrap()
16235    });
16236    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16237
16238    let buffer_1 = project
16239        .update(cx, |project, cx| {
16240            project.open_buffer((worktree_id, "main.rs"), cx)
16241        })
16242        .await
16243        .unwrap();
16244
16245    let multi_buffer = cx.new(|cx| {
16246        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16247        multi_buffer.push_excerpts(
16248            buffer_1.clone(),
16249            [ExcerptRange {
16250                context: Point::new(0, 0)
16251                    ..Point::new(
16252                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16253                        0,
16254                    ),
16255                primary: None,
16256            }],
16257            cx,
16258        );
16259        multi_buffer
16260    });
16261    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16262        Editor::new(
16263            EditorMode::Full,
16264            multi_buffer,
16265            Some(project.clone()),
16266            true,
16267            window,
16268            cx,
16269        )
16270    });
16271
16272    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16273    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16274        enum TestHighlight {}
16275        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16276        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16277        editor.highlight_text::<TestHighlight>(
16278            vec![highlight_range.clone()],
16279            HighlightStyle::color(Hsla::green()),
16280            cx,
16281        );
16282        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16283    });
16284
16285    let full_text = format!("\n\n\n{sample_text}\n");
16286    assert_eq!(
16287        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16288        full_text,
16289    );
16290}
16291
16292#[gpui::test]
16293async fn test_inline_completion_text(cx: &mut TestAppContext) {
16294    init_test(cx, |_| {});
16295
16296    // Simple insertion
16297    assert_highlighted_edits(
16298        "Hello, world!",
16299        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16300        true,
16301        cx,
16302        |highlighted_edits, cx| {
16303            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16304            assert_eq!(highlighted_edits.highlights.len(), 1);
16305            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16306            assert_eq!(
16307                highlighted_edits.highlights[0].1.background_color,
16308                Some(cx.theme().status().created_background)
16309            );
16310        },
16311    )
16312    .await;
16313
16314    // Replacement
16315    assert_highlighted_edits(
16316        "This is a test.",
16317        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16318        false,
16319        cx,
16320        |highlighted_edits, cx| {
16321            assert_eq!(highlighted_edits.text, "That is a test.");
16322            assert_eq!(highlighted_edits.highlights.len(), 1);
16323            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16324            assert_eq!(
16325                highlighted_edits.highlights[0].1.background_color,
16326                Some(cx.theme().status().created_background)
16327            );
16328        },
16329    )
16330    .await;
16331
16332    // Multiple edits
16333    assert_highlighted_edits(
16334        "Hello, world!",
16335        vec![
16336            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16337            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16338        ],
16339        false,
16340        cx,
16341        |highlighted_edits, cx| {
16342            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16343            assert_eq!(highlighted_edits.highlights.len(), 2);
16344            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16345            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16346            assert_eq!(
16347                highlighted_edits.highlights[0].1.background_color,
16348                Some(cx.theme().status().created_background)
16349            );
16350            assert_eq!(
16351                highlighted_edits.highlights[1].1.background_color,
16352                Some(cx.theme().status().created_background)
16353            );
16354        },
16355    )
16356    .await;
16357
16358    // Multiple lines with edits
16359    assert_highlighted_edits(
16360        "First line\nSecond line\nThird line\nFourth line",
16361        vec![
16362            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16363            (
16364                Point::new(2, 0)..Point::new(2, 10),
16365                "New third line".to_string(),
16366            ),
16367            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16368        ],
16369        false,
16370        cx,
16371        |highlighted_edits, cx| {
16372            assert_eq!(
16373                highlighted_edits.text,
16374                "Second modified\nNew third line\nFourth updated line"
16375            );
16376            assert_eq!(highlighted_edits.highlights.len(), 3);
16377            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16378            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16379            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16380            for highlight in &highlighted_edits.highlights {
16381                assert_eq!(
16382                    highlight.1.background_color,
16383                    Some(cx.theme().status().created_background)
16384                );
16385            }
16386        },
16387    )
16388    .await;
16389}
16390
16391#[gpui::test]
16392async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16393    init_test(cx, |_| {});
16394
16395    // Deletion
16396    assert_highlighted_edits(
16397        "Hello, world!",
16398        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16399        true,
16400        cx,
16401        |highlighted_edits, cx| {
16402            assert_eq!(highlighted_edits.text, "Hello, world!");
16403            assert_eq!(highlighted_edits.highlights.len(), 1);
16404            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16405            assert_eq!(
16406                highlighted_edits.highlights[0].1.background_color,
16407                Some(cx.theme().status().deleted_background)
16408            );
16409        },
16410    )
16411    .await;
16412
16413    // Insertion
16414    assert_highlighted_edits(
16415        "Hello, world!",
16416        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16417        true,
16418        cx,
16419        |highlighted_edits, cx| {
16420            assert_eq!(highlighted_edits.highlights.len(), 1);
16421            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16422            assert_eq!(
16423                highlighted_edits.highlights[0].1.background_color,
16424                Some(cx.theme().status().created_background)
16425            );
16426        },
16427    )
16428    .await;
16429}
16430
16431async fn assert_highlighted_edits(
16432    text: &str,
16433    edits: Vec<(Range<Point>, String)>,
16434    include_deletions: bool,
16435    cx: &mut TestAppContext,
16436    assertion_fn: impl Fn(HighlightedText, &App),
16437) {
16438    let window = cx.add_window(|window, cx| {
16439        let buffer = MultiBuffer::build_simple(text, cx);
16440        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16441    });
16442    let cx = &mut VisualTestContext::from_window(*window, cx);
16443
16444    let (buffer, snapshot) = window
16445        .update(cx, |editor, _window, cx| {
16446            (
16447                editor.buffer().clone(),
16448                editor.buffer().read(cx).snapshot(cx),
16449            )
16450        })
16451        .unwrap();
16452
16453    let edits = edits
16454        .into_iter()
16455        .map(|(range, edit)| {
16456            (
16457                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16458                edit,
16459            )
16460        })
16461        .collect::<Vec<_>>();
16462
16463    let text_anchor_edits = edits
16464        .clone()
16465        .into_iter()
16466        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16467        .collect::<Vec<_>>();
16468
16469    let edit_preview = window
16470        .update(cx, |_, _window, cx| {
16471            buffer
16472                .read(cx)
16473                .as_singleton()
16474                .unwrap()
16475                .read(cx)
16476                .preview_edits(text_anchor_edits.into(), cx)
16477        })
16478        .unwrap()
16479        .await;
16480
16481    cx.update(|_window, cx| {
16482        let highlighted_edits = inline_completion_edit_text(
16483            &snapshot.as_singleton().unwrap().2,
16484            &edits,
16485            &edit_preview,
16486            include_deletions,
16487            cx,
16488        );
16489        assertion_fn(highlighted_edits, cx)
16490    });
16491}
16492
16493#[gpui::test]
16494async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16495    init_test(cx, |_| {});
16496    let capabilities = lsp::ServerCapabilities {
16497        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16498            prepare_provider: Some(true),
16499            work_done_progress_options: Default::default(),
16500        })),
16501        ..Default::default()
16502    };
16503    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16504
16505    cx.set_state(indoc! {"
16506        struct Fˇoo {}
16507    "});
16508
16509    cx.update_editor(|editor, _, cx| {
16510        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16511        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16512        editor.highlight_background::<DocumentHighlightRead>(
16513            &[highlight_range],
16514            |c| c.editor_document_highlight_read_background,
16515            cx,
16516        );
16517    });
16518
16519    let mut prepare_rename_handler =
16520        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16521            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16522                start: lsp::Position {
16523                    line: 0,
16524                    character: 7,
16525                },
16526                end: lsp::Position {
16527                    line: 0,
16528                    character: 10,
16529                },
16530            })))
16531        });
16532    let prepare_rename_task = cx
16533        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16534        .expect("Prepare rename was not started");
16535    prepare_rename_handler.next().await.unwrap();
16536    prepare_rename_task.await.expect("Prepare rename failed");
16537
16538    let mut rename_handler =
16539        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16540            let edit = lsp::TextEdit {
16541                range: lsp::Range {
16542                    start: lsp::Position {
16543                        line: 0,
16544                        character: 7,
16545                    },
16546                    end: lsp::Position {
16547                        line: 0,
16548                        character: 10,
16549                    },
16550                },
16551                new_text: "FooRenamed".to_string(),
16552            };
16553            Ok(Some(lsp::WorkspaceEdit::new(
16554                // Specify the same edit twice
16555                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16556            )))
16557        });
16558    let rename_task = cx
16559        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16560        .expect("Confirm rename was not started");
16561    rename_handler.next().await.unwrap();
16562    rename_task.await.expect("Confirm rename failed");
16563    cx.run_until_parked();
16564
16565    // Despite two edits, only one is actually applied as those are identical
16566    cx.assert_editor_state(indoc! {"
16567        struct FooRenamedˇ {}
16568    "});
16569}
16570
16571#[gpui::test]
16572async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16573    init_test(cx, |_| {});
16574    // These capabilities indicate that the server does not support prepare rename.
16575    let capabilities = lsp::ServerCapabilities {
16576        rename_provider: Some(lsp::OneOf::Left(true)),
16577        ..Default::default()
16578    };
16579    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16580
16581    cx.set_state(indoc! {"
16582        struct Fˇoo {}
16583    "});
16584
16585    cx.update_editor(|editor, _window, cx| {
16586        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16587        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16588        editor.highlight_background::<DocumentHighlightRead>(
16589            &[highlight_range],
16590            |c| c.editor_document_highlight_read_background,
16591            cx,
16592        );
16593    });
16594
16595    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16596        .expect("Prepare rename was not started")
16597        .await
16598        .expect("Prepare rename failed");
16599
16600    let mut rename_handler =
16601        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16602            let edit = lsp::TextEdit {
16603                range: lsp::Range {
16604                    start: lsp::Position {
16605                        line: 0,
16606                        character: 7,
16607                    },
16608                    end: lsp::Position {
16609                        line: 0,
16610                        character: 10,
16611                    },
16612                },
16613                new_text: "FooRenamed".to_string(),
16614            };
16615            Ok(Some(lsp::WorkspaceEdit::new(
16616                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16617            )))
16618        });
16619    let rename_task = cx
16620        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16621        .expect("Confirm rename was not started");
16622    rename_handler.next().await.unwrap();
16623    rename_task.await.expect("Confirm rename failed");
16624    cx.run_until_parked();
16625
16626    // Correct range is renamed, as `surrounding_word` is used to find it.
16627    cx.assert_editor_state(indoc! {"
16628        struct FooRenamedˇ {}
16629    "});
16630}
16631
16632#[gpui::test]
16633async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16634    init_test(cx, |_| {});
16635    let mut cx = EditorTestContext::new(cx).await;
16636
16637    let language = Arc::new(
16638        Language::new(
16639            LanguageConfig::default(),
16640            Some(tree_sitter_html::LANGUAGE.into()),
16641        )
16642        .with_brackets_query(
16643            r#"
16644            ("<" @open "/>" @close)
16645            ("</" @open ">" @close)
16646            ("<" @open ">" @close)
16647            ("\"" @open "\"" @close)
16648            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16649        "#,
16650        )
16651        .unwrap(),
16652    );
16653    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16654
16655    cx.set_state(indoc! {"
16656        <span>ˇ</span>
16657    "});
16658    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16659    cx.assert_editor_state(indoc! {"
16660        <span>
16661        ˇ
16662        </span>
16663    "});
16664
16665    cx.set_state(indoc! {"
16666        <span><span></span>ˇ</span>
16667    "});
16668    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16669    cx.assert_editor_state(indoc! {"
16670        <span><span></span>
16671        ˇ</span>
16672    "});
16673
16674    cx.set_state(indoc! {"
16675        <span>ˇ
16676        </span>
16677    "});
16678    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16679    cx.assert_editor_state(indoc! {"
16680        <span>
16681        ˇ
16682        </span>
16683    "});
16684}
16685
16686fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16687    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16688    point..point
16689}
16690
16691fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16692    let (text, ranges) = marked_text_ranges(marked_text, true);
16693    assert_eq!(editor.text(cx), text);
16694    assert_eq!(
16695        editor.selections.ranges(cx),
16696        ranges,
16697        "Assert selections are {}",
16698        marked_text
16699    );
16700}
16701
16702pub fn handle_signature_help_request(
16703    cx: &mut EditorLspTestContext,
16704    mocked_response: lsp::SignatureHelp,
16705) -> impl Future<Output = ()> {
16706    let mut request =
16707        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16708            let mocked_response = mocked_response.clone();
16709            async move { Ok(Some(mocked_response)) }
16710        });
16711
16712    async move {
16713        request.next().await;
16714    }
16715}
16716
16717/// Handle completion request passing a marked string specifying where the completion
16718/// should be triggered from using '|' character, what range should be replaced, and what completions
16719/// should be returned using '<' and '>' to delimit the range
16720pub fn handle_completion_request(
16721    cx: &mut EditorLspTestContext,
16722    marked_string: &str,
16723    completions: Vec<&'static str>,
16724    counter: Arc<AtomicUsize>,
16725) -> impl Future<Output = ()> {
16726    let complete_from_marker: TextRangeMarker = '|'.into();
16727    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16728    let (_, mut marked_ranges) = marked_text_ranges_by(
16729        marked_string,
16730        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16731    );
16732
16733    let complete_from_position =
16734        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16735    let replace_range =
16736        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16737
16738    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16739        let completions = completions.clone();
16740        counter.fetch_add(1, atomic::Ordering::Release);
16741        async move {
16742            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16743            assert_eq!(
16744                params.text_document_position.position,
16745                complete_from_position
16746            );
16747            Ok(Some(lsp::CompletionResponse::Array(
16748                completions
16749                    .iter()
16750                    .map(|completion_text| lsp::CompletionItem {
16751                        label: completion_text.to_string(),
16752                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16753                            range: replace_range,
16754                            new_text: completion_text.to_string(),
16755                        })),
16756                        ..Default::default()
16757                    })
16758                    .collect(),
16759            )))
16760        }
16761    });
16762
16763    async move {
16764        request.next().await;
16765    }
16766}
16767
16768fn handle_resolve_completion_request(
16769    cx: &mut EditorLspTestContext,
16770    edits: Option<Vec<(&'static str, &'static str)>>,
16771) -> impl Future<Output = ()> {
16772    let edits = edits.map(|edits| {
16773        edits
16774            .iter()
16775            .map(|(marked_string, new_text)| {
16776                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16777                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16778                lsp::TextEdit::new(replace_range, new_text.to_string())
16779            })
16780            .collect::<Vec<_>>()
16781    });
16782
16783    let mut request =
16784        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16785            let edits = edits.clone();
16786            async move {
16787                Ok(lsp::CompletionItem {
16788                    additional_text_edits: edits,
16789                    ..Default::default()
16790                })
16791            }
16792        });
16793
16794    async move {
16795        request.next().await;
16796    }
16797}
16798
16799pub(crate) fn update_test_language_settings(
16800    cx: &mut TestAppContext,
16801    f: impl Fn(&mut AllLanguageSettingsContent),
16802) {
16803    cx.update(|cx| {
16804        SettingsStore::update_global(cx, |store, cx| {
16805            store.update_user_settings::<AllLanguageSettings>(cx, f);
16806        });
16807    });
16808}
16809
16810pub(crate) fn update_test_project_settings(
16811    cx: &mut TestAppContext,
16812    f: impl Fn(&mut ProjectSettings),
16813) {
16814    cx.update(|cx| {
16815        SettingsStore::update_global(cx, |store, cx| {
16816            store.update_user_settings::<ProjectSettings>(cx, f);
16817        });
16818    });
16819}
16820
16821pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16822    cx.update(|cx| {
16823        assets::Assets.load_test_fonts(cx);
16824        let store = SettingsStore::test(cx);
16825        cx.set_global(store);
16826        theme::init(theme::LoadThemes::JustBase, cx);
16827        release_channel::init(SemanticVersion::default(), cx);
16828        client::init_settings(cx);
16829        language::init(cx);
16830        Project::init_settings(cx);
16831        workspace::init_settings(cx);
16832        crate::init(cx);
16833    });
16834
16835    update_test_language_settings(cx, f);
16836}
16837
16838#[track_caller]
16839fn assert_hunk_revert(
16840    not_reverted_text_with_selections: &str,
16841    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16842    expected_reverted_text_with_selections: &str,
16843    base_text: &str,
16844    cx: &mut EditorLspTestContext,
16845) {
16846    cx.set_state(not_reverted_text_with_selections);
16847    cx.set_head_text(base_text);
16848    cx.clear_index_text();
16849    cx.executor().run_until_parked();
16850
16851    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16852        let snapshot = editor.snapshot(window, cx);
16853        let reverted_hunk_statuses = snapshot
16854            .buffer_snapshot
16855            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16856            .map(|hunk| hunk.status())
16857            .collect::<Vec<_>>();
16858
16859        editor.git_restore(&Default::default(), window, cx);
16860        reverted_hunk_statuses
16861    });
16862    cx.executor().run_until_parked();
16863    cx.assert_editor_state(expected_reverted_text_with_selections);
16864    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16865}