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, DiffHunkSecondaryStatus, 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 delete_to_beg = DeleteToBeginningOfLine {
 1518        stop_at_indent: false,
 1519    };
 1520
 1521    let move_to_end = MoveToEndOfLine {
 1522        stop_at_soft_wraps: true,
 1523    };
 1524
 1525    let editor = cx.add_window(|window, cx| {
 1526        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1527        build_editor(buffer, window, cx)
 1528    });
 1529    _ = editor.update(cx, |editor, window, cx| {
 1530        editor.change_selections(None, window, cx, |s| {
 1531            s.select_display_ranges([
 1532                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1533                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1534            ]);
 1535        });
 1536    });
 1537
 1538    _ = editor.update(cx, |editor, window, cx| {
 1539        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1540        assert_eq!(
 1541            editor.selections.display_ranges(cx),
 1542            &[
 1543                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1544                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1545            ]
 1546        );
 1547    });
 1548
 1549    _ = editor.update(cx, |editor, window, cx| {
 1550        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1551        assert_eq!(
 1552            editor.selections.display_ranges(cx),
 1553            &[
 1554                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1555                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1556            ]
 1557        );
 1558    });
 1559
 1560    _ = editor.update(cx, |editor, window, cx| {
 1561        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1562        assert_eq!(
 1563            editor.selections.display_ranges(cx),
 1564            &[
 1565                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1566                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1567            ]
 1568        );
 1569    });
 1570
 1571    _ = editor.update(cx, |editor, window, cx| {
 1572        editor.move_to_end_of_line(&move_to_end, window, cx);
 1573        assert_eq!(
 1574            editor.selections.display_ranges(cx),
 1575            &[
 1576                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1577                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1578            ]
 1579        );
 1580    });
 1581
 1582    // Moving to the end of line again is a no-op.
 1583    _ = editor.update(cx, |editor, window, cx| {
 1584        editor.move_to_end_of_line(&move_to_end, window, cx);
 1585        assert_eq!(
 1586            editor.selections.display_ranges(cx),
 1587            &[
 1588                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1589                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1590            ]
 1591        );
 1592    });
 1593
 1594    _ = editor.update(cx, |editor, window, cx| {
 1595        editor.move_left(&MoveLeft, window, cx);
 1596        editor.select_to_beginning_of_line(
 1597            &SelectToBeginningOfLine {
 1598                stop_at_soft_wraps: true,
 1599                stop_at_indent: true,
 1600            },
 1601            window,
 1602            cx,
 1603        );
 1604        assert_eq!(
 1605            editor.selections.display_ranges(cx),
 1606            &[
 1607                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1608                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1609            ]
 1610        );
 1611    });
 1612
 1613    _ = editor.update(cx, |editor, window, cx| {
 1614        editor.select_to_beginning_of_line(
 1615            &SelectToBeginningOfLine {
 1616                stop_at_soft_wraps: true,
 1617                stop_at_indent: true,
 1618            },
 1619            window,
 1620            cx,
 1621        );
 1622        assert_eq!(
 1623            editor.selections.display_ranges(cx),
 1624            &[
 1625                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1626                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1627            ]
 1628        );
 1629    });
 1630
 1631    _ = editor.update(cx, |editor, window, cx| {
 1632        editor.select_to_beginning_of_line(
 1633            &SelectToBeginningOfLine {
 1634                stop_at_soft_wraps: true,
 1635                stop_at_indent: true,
 1636            },
 1637            window,
 1638            cx,
 1639        );
 1640        assert_eq!(
 1641            editor.selections.display_ranges(cx),
 1642            &[
 1643                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1644                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1645            ]
 1646        );
 1647    });
 1648
 1649    _ = editor.update(cx, |editor, window, cx| {
 1650        editor.select_to_end_of_line(
 1651            &SelectToEndOfLine {
 1652                stop_at_soft_wraps: true,
 1653            },
 1654            window,
 1655            cx,
 1656        );
 1657        assert_eq!(
 1658            editor.selections.display_ranges(cx),
 1659            &[
 1660                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1661                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1662            ]
 1663        );
 1664    });
 1665
 1666    _ = editor.update(cx, |editor, window, cx| {
 1667        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1668        assert_eq!(editor.display_text(cx), "ab\n  de");
 1669        assert_eq!(
 1670            editor.selections.display_ranges(cx),
 1671            &[
 1672                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1673                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1674            ]
 1675        );
 1676    });
 1677
 1678    _ = editor.update(cx, |editor, window, cx| {
 1679        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1680        assert_eq!(editor.display_text(cx), "\n");
 1681        assert_eq!(
 1682            editor.selections.display_ranges(cx),
 1683            &[
 1684                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1685                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1686            ]
 1687        );
 1688    });
 1689}
 1690
 1691#[gpui::test]
 1692fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1693    init_test(cx, |_| {});
 1694    let move_to_beg = MoveToBeginningOfLine {
 1695        stop_at_soft_wraps: false,
 1696        stop_at_indent: false,
 1697    };
 1698
 1699    let move_to_end = MoveToEndOfLine {
 1700        stop_at_soft_wraps: false,
 1701    };
 1702
 1703    let editor = cx.add_window(|window, cx| {
 1704        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1705        build_editor(buffer, window, cx)
 1706    });
 1707
 1708    _ = editor.update(cx, |editor, window, cx| {
 1709        editor.set_wrap_width(Some(140.0.into()), cx);
 1710
 1711        // We expect the following lines after wrapping
 1712        // ```
 1713        // thequickbrownfox
 1714        // jumpedoverthelazydo
 1715        // gs
 1716        // ```
 1717        // The final `gs` was soft-wrapped onto a new line.
 1718        assert_eq!(
 1719            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1720            editor.display_text(cx),
 1721        );
 1722
 1723        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1724        // Start the cursor at the `k` on the first line
 1725        editor.change_selections(None, window, cx, |s| {
 1726            s.select_display_ranges([
 1727                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1728            ]);
 1729        });
 1730
 1731        // Moving to the beginning of the line should put us at the beginning of the line.
 1732        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1733        assert_eq!(
 1734            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1735            editor.selections.display_ranges(cx)
 1736        );
 1737
 1738        // Moving to the end of the line should put us at the end of the line.
 1739        editor.move_to_end_of_line(&move_to_end, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1746        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1747        editor.change_selections(None, window, cx, |s| {
 1748            s.select_display_ranges([
 1749                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1750            ]);
 1751        });
 1752
 1753        // Moving to the beginning of the line should put us at the start of the second line of
 1754        // display text, i.e., the `j`.
 1755        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1756        assert_eq!(
 1757            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1758            editor.selections.display_ranges(cx)
 1759        );
 1760
 1761        // Moving to the beginning of the line again should be a no-op.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1769        // next display line.
 1770        editor.move_to_end_of_line(&move_to_end, window, cx);
 1771        assert_eq!(
 1772            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1773            editor.selections.display_ranges(cx)
 1774        );
 1775
 1776        // Moving to the end of the line again should be a no-op.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782    });
 1783}
 1784
 1785#[gpui::test]
 1786fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1787    init_test(cx, |_| {});
 1788
 1789    let move_to_beg = MoveToBeginningOfLine {
 1790        stop_at_soft_wraps: true,
 1791        stop_at_indent: true,
 1792    };
 1793
 1794    let select_to_beg = SelectToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let delete_to_beg = DeleteToBeginningOfLine {
 1800        stop_at_indent: true,
 1801    };
 1802
 1803    let move_to_end = MoveToEndOfLine {
 1804        stop_at_soft_wraps: false,
 1805    };
 1806
 1807    let editor = cx.add_window(|window, cx| {
 1808        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1809        build_editor(buffer, window, cx)
 1810    });
 1811
 1812    _ = editor.update(cx, |editor, window, cx| {
 1813        editor.change_selections(None, window, cx, |s| {
 1814            s.select_display_ranges([
 1815                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1816                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1817            ]);
 1818        });
 1819
 1820        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1821        // and the second cursor at the first non-whitespace character in the line.
 1822        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1823        assert_eq!(
 1824            editor.selections.display_ranges(cx),
 1825            &[
 1826                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1827                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1828            ]
 1829        );
 1830
 1831        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1832        // and should move the second cursor to the beginning of the line.
 1833        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1834        assert_eq!(
 1835            editor.selections.display_ranges(cx),
 1836            &[
 1837                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1838                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1839            ]
 1840        );
 1841
 1842        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1843        // and should move the second cursor back to the first non-whitespace character in the line.
 1844        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1845        assert_eq!(
 1846            editor.selections.display_ranges(cx),
 1847            &[
 1848                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1849                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1850            ]
 1851        );
 1852
 1853        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1854        // and to the first non-whitespace character in the line for the second cursor.
 1855        editor.move_to_end_of_line(&move_to_end, window, cx);
 1856        editor.move_left(&MoveLeft, window, cx);
 1857        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1858        assert_eq!(
 1859            editor.selections.display_ranges(cx),
 1860            &[
 1861                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1862                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1863            ]
 1864        );
 1865
 1866        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1867        // and should select to the beginning of the line for the second cursor.
 1868        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1869        assert_eq!(
 1870            editor.selections.display_ranges(cx),
 1871            &[
 1872                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1873                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1874            ]
 1875        );
 1876
 1877        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1878        // and should delete to the first non-whitespace character in the line for the second cursor.
 1879        editor.move_to_end_of_line(&move_to_end, window, cx);
 1880        editor.move_left(&MoveLeft, window, cx);
 1881        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1882        assert_eq!(editor.text(cx), "c\n  f");
 1883    });
 1884}
 1885
 1886#[gpui::test]
 1887fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1888    init_test(cx, |_| {});
 1889
 1890    let editor = cx.add_window(|window, cx| {
 1891        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1892        build_editor(buffer, window, cx)
 1893    });
 1894    _ = editor.update(cx, |editor, window, cx| {
 1895        editor.change_selections(None, window, cx, |s| {
 1896            s.select_display_ranges([
 1897                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1898                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1899            ])
 1900        });
 1901
 1902        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1903        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1904
 1905        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1906        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1907
 1908        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1909        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1918        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1921        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1924        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1925
 1926        editor.move_right(&MoveRight, window, cx);
 1927        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1928        assert_selection_ranges(
 1929            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1930            editor,
 1931            cx,
 1932        );
 1933
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1942        assert_selection_ranges(
 1943            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947    });
 1948}
 1949
 1950#[gpui::test]
 1951fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1952    init_test(cx, |_| {});
 1953
 1954    let editor = cx.add_window(|window, cx| {
 1955        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1956        build_editor(buffer, window, cx)
 1957    });
 1958
 1959    _ = editor.update(cx, |editor, window, cx| {
 1960        editor.set_wrap_width(Some(140.0.into()), cx);
 1961        assert_eq!(
 1962            editor.display_text(cx),
 1963            "use one::{\n    two::three::\n    four::five\n};"
 1964        );
 1965
 1966        editor.change_selections(None, window, cx, |s| {
 1967            s.select_display_ranges([
 1968                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1969            ]);
 1970        });
 1971
 1972        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1973        assert_eq!(
 1974            editor.selections.display_ranges(cx),
 1975            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1976        );
 1977
 1978        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1979        assert_eq!(
 1980            editor.selections.display_ranges(cx),
 1981            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1982        );
 1983
 1984        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1985        assert_eq!(
 1986            editor.selections.display_ranges(cx),
 1987            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1988        );
 1989
 1990        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1991        assert_eq!(
 1992            editor.selections.display_ranges(cx),
 1993            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1994        );
 1995
 1996        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1997        assert_eq!(
 1998            editor.selections.display_ranges(cx),
 1999            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2000        );
 2001
 2002        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2003        assert_eq!(
 2004            editor.selections.display_ranges(cx),
 2005            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2006        );
 2007    });
 2008}
 2009
 2010#[gpui::test]
 2011async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2012    init_test(cx, |_| {});
 2013    let mut cx = EditorTestContext::new(cx).await;
 2014
 2015    let line_height = cx.editor(|editor, window, _| {
 2016        editor
 2017            .style()
 2018            .unwrap()
 2019            .text
 2020            .line_height_in_pixels(window.rem_size())
 2021    });
 2022    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2023
 2024    cx.set_state(
 2025        &r#"ˇone
 2026        two
 2027
 2028        three
 2029        fourˇ
 2030        five
 2031
 2032        six"#
 2033            .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, window, cx| {
 2037        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2038    });
 2039    cx.assert_editor_state(
 2040        &r#"one
 2041        two
 2042        ˇ
 2043        three
 2044        four
 2045        five
 2046        ˇ
 2047        six"#
 2048            .unindent(),
 2049    );
 2050
 2051    cx.update_editor(|editor, window, cx| {
 2052        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2053    });
 2054    cx.assert_editor_state(
 2055        &r#"one
 2056        two
 2057
 2058        three
 2059        four
 2060        five
 2061        ˇ
 2062        sixˇ"#
 2063            .unindent(),
 2064    );
 2065
 2066    cx.update_editor(|editor, window, cx| {
 2067        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2068    });
 2069    cx.assert_editor_state(
 2070        &r#"one
 2071        two
 2072
 2073        three
 2074        four
 2075        five
 2076
 2077        sixˇ"#
 2078            .unindent(),
 2079    );
 2080
 2081    cx.update_editor(|editor, window, cx| {
 2082        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2083    });
 2084    cx.assert_editor_state(
 2085        &r#"one
 2086        two
 2087
 2088        three
 2089        four
 2090        five
 2091        ˇ
 2092        six"#
 2093            .unindent(),
 2094    );
 2095
 2096    cx.update_editor(|editor, window, cx| {
 2097        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2098    });
 2099    cx.assert_editor_state(
 2100        &r#"one
 2101        two
 2102        ˇ
 2103        three
 2104        four
 2105        five
 2106
 2107        six"#
 2108            .unindent(),
 2109    );
 2110
 2111    cx.update_editor(|editor, window, cx| {
 2112        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2113    });
 2114    cx.assert_editor_state(
 2115        &r#"ˇone
 2116        two
 2117
 2118        three
 2119        four
 2120        five
 2121
 2122        six"#
 2123            .unindent(),
 2124    );
 2125}
 2126
 2127#[gpui::test]
 2128async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2129    init_test(cx, |_| {});
 2130    let mut cx = EditorTestContext::new(cx).await;
 2131    let line_height = cx.editor(|editor, window, _| {
 2132        editor
 2133            .style()
 2134            .unwrap()
 2135            .text
 2136            .line_height_in_pixels(window.rem_size())
 2137    });
 2138    let window = cx.window;
 2139    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2140
 2141    cx.set_state(
 2142        r#"ˇone
 2143        two
 2144        three
 2145        four
 2146        five
 2147        six
 2148        seven
 2149        eight
 2150        nine
 2151        ten
 2152        "#,
 2153    );
 2154
 2155    cx.update_editor(|editor, window, cx| {
 2156        assert_eq!(
 2157            editor.snapshot(window, cx).scroll_position(),
 2158            gpui::Point::new(0., 0.)
 2159        );
 2160        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 3.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 6.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 3.)
 2174        );
 2175
 2176        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2177        assert_eq!(
 2178            editor.snapshot(window, cx).scroll_position(),
 2179            gpui::Point::new(0., 1.)
 2180        );
 2181        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 3.)
 2185        );
 2186    });
 2187}
 2188
 2189#[gpui::test]
 2190async fn test_autoscroll(cx: &mut TestAppContext) {
 2191    init_test(cx, |_| {});
 2192    let mut cx = EditorTestContext::new(cx).await;
 2193
 2194    let line_height = cx.update_editor(|editor, window, cx| {
 2195        editor.set_vertical_scroll_margin(2, cx);
 2196        editor
 2197            .style()
 2198            .unwrap()
 2199            .text
 2200            .line_height_in_pixels(window.rem_size())
 2201    });
 2202    let window = cx.window;
 2203    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2204
 2205    cx.set_state(
 2206        r#"ˇone
 2207            two
 2208            three
 2209            four
 2210            five
 2211            six
 2212            seven
 2213            eight
 2214            nine
 2215            ten
 2216        "#,
 2217    );
 2218    cx.update_editor(|editor, window, cx| {
 2219        assert_eq!(
 2220            editor.snapshot(window, cx).scroll_position(),
 2221            gpui::Point::new(0., 0.0)
 2222        );
 2223    });
 2224
 2225    // Add a cursor below the visible area. Since both cursors cannot fit
 2226    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2227    // allows the vertical scroll margin below that cursor.
 2228    cx.update_editor(|editor, window, cx| {
 2229        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2230            selections.select_ranges([
 2231                Point::new(0, 0)..Point::new(0, 0),
 2232                Point::new(6, 0)..Point::new(6, 0),
 2233            ]);
 2234        })
 2235    });
 2236    cx.update_editor(|editor, window, cx| {
 2237        assert_eq!(
 2238            editor.snapshot(window, cx).scroll_position(),
 2239            gpui::Point::new(0., 3.0)
 2240        );
 2241    });
 2242
 2243    // Move down. The editor cursor scrolls down to track the newest cursor.
 2244    cx.update_editor(|editor, window, cx| {
 2245        editor.move_down(&Default::default(), window, cx);
 2246    });
 2247    cx.update_editor(|editor, window, cx| {
 2248        assert_eq!(
 2249            editor.snapshot(window, cx).scroll_position(),
 2250            gpui::Point::new(0., 4.0)
 2251        );
 2252    });
 2253
 2254    // Add a cursor above the visible area. Since both cursors fit on screen,
 2255    // the editor scrolls to show both.
 2256    cx.update_editor(|editor, window, cx| {
 2257        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2258            selections.select_ranges([
 2259                Point::new(1, 0)..Point::new(1, 0),
 2260                Point::new(6, 0)..Point::new(6, 0),
 2261            ]);
 2262        })
 2263    });
 2264    cx.update_editor(|editor, window, cx| {
 2265        assert_eq!(
 2266            editor.snapshot(window, cx).scroll_position(),
 2267            gpui::Point::new(0., 1.0)
 2268        );
 2269    });
 2270}
 2271
 2272#[gpui::test]
 2273async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2274    init_test(cx, |_| {});
 2275    let mut cx = EditorTestContext::new(cx).await;
 2276
 2277    let line_height = cx.editor(|editor, window, _cx| {
 2278        editor
 2279            .style()
 2280            .unwrap()
 2281            .text
 2282            .line_height_in_pixels(window.rem_size())
 2283    });
 2284    let window = cx.window;
 2285    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2286    cx.set_state(
 2287        &r#"
 2288        ˇone
 2289        two
 2290        threeˇ
 2291        four
 2292        five
 2293        six
 2294        seven
 2295        eight
 2296        nine
 2297        ten
 2298        "#
 2299        .unindent(),
 2300    );
 2301
 2302    cx.update_editor(|editor, window, cx| {
 2303        editor.move_page_down(&MovePageDown::default(), window, cx)
 2304    });
 2305    cx.assert_editor_state(
 2306        &r#"
 2307        one
 2308        two
 2309        three
 2310        ˇfour
 2311        five
 2312        sixˇ
 2313        seven
 2314        eight
 2315        nine
 2316        ten
 2317        "#
 2318        .unindent(),
 2319    );
 2320
 2321    cx.update_editor(|editor, window, cx| {
 2322        editor.move_page_down(&MovePageDown::default(), window, cx)
 2323    });
 2324    cx.assert_editor_state(
 2325        &r#"
 2326        one
 2327        two
 2328        three
 2329        four
 2330        five
 2331        six
 2332        ˇseven
 2333        eight
 2334        nineˇ
 2335        ten
 2336        "#
 2337        .unindent(),
 2338    );
 2339
 2340    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2341    cx.assert_editor_state(
 2342        &r#"
 2343        one
 2344        two
 2345        three
 2346        ˇfour
 2347        five
 2348        sixˇ
 2349        seven
 2350        eight
 2351        nine
 2352        ten
 2353        "#
 2354        .unindent(),
 2355    );
 2356
 2357    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2358    cx.assert_editor_state(
 2359        &r#"
 2360        ˇone
 2361        two
 2362        threeˇ
 2363        four
 2364        five
 2365        six
 2366        seven
 2367        eight
 2368        nine
 2369        ten
 2370        "#
 2371        .unindent(),
 2372    );
 2373
 2374    // Test select collapsing
 2375    cx.update_editor(|editor, window, cx| {
 2376        editor.move_page_down(&MovePageDown::default(), window, cx);
 2377        editor.move_page_down(&MovePageDown::default(), window, cx);
 2378        editor.move_page_down(&MovePageDown::default(), window, cx);
 2379    });
 2380    cx.assert_editor_state(
 2381        &r#"
 2382        one
 2383        two
 2384        three
 2385        four
 2386        five
 2387        six
 2388        seven
 2389        eight
 2390        nine
 2391        ˇten
 2392        ˇ"#
 2393        .unindent(),
 2394    );
 2395}
 2396
 2397#[gpui::test]
 2398async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2399    init_test(cx, |_| {});
 2400    let mut cx = EditorTestContext::new(cx).await;
 2401    cx.set_state("one «two threeˇ» four");
 2402    cx.update_editor(|editor, window, cx| {
 2403        editor.delete_to_beginning_of_line(
 2404            &DeleteToBeginningOfLine {
 2405                stop_at_indent: false,
 2406            },
 2407            window,
 2408            cx,
 2409        );
 2410        assert_eq!(editor.text(cx), " four");
 2411    });
 2412}
 2413
 2414#[gpui::test]
 2415fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2416    init_test(cx, |_| {});
 2417
 2418    let editor = cx.add_window(|window, cx| {
 2419        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2420        build_editor(buffer.clone(), window, cx)
 2421    });
 2422
 2423    _ = editor.update(cx, |editor, window, cx| {
 2424        editor.change_selections(None, window, cx, |s| {
 2425            s.select_display_ranges([
 2426                // an empty selection - the preceding word fragment is deleted
 2427                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2428                // characters selected - they are deleted
 2429                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2430            ])
 2431        });
 2432        editor.delete_to_previous_word_start(
 2433            &DeleteToPreviousWordStart {
 2434                ignore_newlines: false,
 2435            },
 2436            window,
 2437            cx,
 2438        );
 2439        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2440    });
 2441
 2442    _ = editor.update(cx, |editor, window, cx| {
 2443        editor.change_selections(None, window, cx, |s| {
 2444            s.select_display_ranges([
 2445                // an empty selection - the following word fragment is deleted
 2446                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2447                // characters selected - they are deleted
 2448                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2449            ])
 2450        });
 2451        editor.delete_to_next_word_end(
 2452            &DeleteToNextWordEnd {
 2453                ignore_newlines: false,
 2454            },
 2455            window,
 2456            cx,
 2457        );
 2458        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2459    });
 2460}
 2461
 2462#[gpui::test]
 2463fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2464    init_test(cx, |_| {});
 2465
 2466    let editor = cx.add_window(|window, cx| {
 2467        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2468        build_editor(buffer.clone(), window, cx)
 2469    });
 2470    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2471        ignore_newlines: false,
 2472    };
 2473    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2474        ignore_newlines: true,
 2475    };
 2476
 2477    _ = editor.update(cx, |editor, window, cx| {
 2478        editor.change_selections(None, window, cx, |s| {
 2479            s.select_display_ranges([
 2480                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2481            ])
 2482        });
 2483        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2484        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2485        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2486        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2487        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2488        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2495    });
 2496}
 2497
 2498#[gpui::test]
 2499fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2500    init_test(cx, |_| {});
 2501
 2502    let editor = cx.add_window(|window, cx| {
 2503        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2504        build_editor(buffer.clone(), window, cx)
 2505    });
 2506    let del_to_next_word_end = DeleteToNextWordEnd {
 2507        ignore_newlines: false,
 2508    };
 2509    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2510        ignore_newlines: true,
 2511    };
 2512
 2513    _ = editor.update(cx, |editor, window, cx| {
 2514        editor.change_selections(None, window, cx, |s| {
 2515            s.select_display_ranges([
 2516                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2517            ])
 2518        });
 2519        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2520        assert_eq!(
 2521            editor.buffer.read(cx).read(cx).text(),
 2522            "one\n   two\nthree\n   four"
 2523        );
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2536        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2537        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2538        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2539        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2540    });
 2541}
 2542
 2543#[gpui::test]
 2544fn test_newline(cx: &mut TestAppContext) {
 2545    init_test(cx, |_| {});
 2546
 2547    let editor = cx.add_window(|window, cx| {
 2548        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2549        build_editor(buffer.clone(), window, cx)
 2550    });
 2551
 2552    _ = editor.update(cx, |editor, window, cx| {
 2553        editor.change_selections(None, window, cx, |s| {
 2554            s.select_display_ranges([
 2555                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2556                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2557                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2558            ])
 2559        });
 2560
 2561        editor.newline(&Newline, window, cx);
 2562        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2563    });
 2564}
 2565
 2566#[gpui::test]
 2567fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2568    init_test(cx, |_| {});
 2569
 2570    let editor = cx.add_window(|window, cx| {
 2571        let buffer = MultiBuffer::build_simple(
 2572            "
 2573                a
 2574                b(
 2575                    X
 2576                )
 2577                c(
 2578                    X
 2579                )
 2580            "
 2581            .unindent()
 2582            .as_str(),
 2583            cx,
 2584        );
 2585        let mut editor = build_editor(buffer.clone(), window, cx);
 2586        editor.change_selections(None, window, cx, |s| {
 2587            s.select_ranges([
 2588                Point::new(2, 4)..Point::new(2, 5),
 2589                Point::new(5, 4)..Point::new(5, 5),
 2590            ])
 2591        });
 2592        editor
 2593    });
 2594
 2595    _ = editor.update(cx, |editor, window, cx| {
 2596        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2597        editor.buffer.update(cx, |buffer, cx| {
 2598            buffer.edit(
 2599                [
 2600                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2601                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2602                ],
 2603                None,
 2604                cx,
 2605            );
 2606            assert_eq!(
 2607                buffer.read(cx).text(),
 2608                "
 2609                    a
 2610                    b()
 2611                    c()
 2612                "
 2613                .unindent()
 2614            );
 2615        });
 2616        assert_eq!(
 2617            editor.selections.ranges(cx),
 2618            &[
 2619                Point::new(1, 2)..Point::new(1, 2),
 2620                Point::new(2, 2)..Point::new(2, 2),
 2621            ],
 2622        );
 2623
 2624        editor.newline(&Newline, window, cx);
 2625        assert_eq!(
 2626            editor.text(cx),
 2627            "
 2628                a
 2629                b(
 2630                )
 2631                c(
 2632                )
 2633            "
 2634            .unindent()
 2635        );
 2636
 2637        // The selections are moved after the inserted newlines
 2638        assert_eq!(
 2639            editor.selections.ranges(cx),
 2640            &[
 2641                Point::new(2, 0)..Point::new(2, 0),
 2642                Point::new(4, 0)..Point::new(4, 0),
 2643            ],
 2644        );
 2645    });
 2646}
 2647
 2648#[gpui::test]
 2649async fn test_newline_above(cx: &mut TestAppContext) {
 2650    init_test(cx, |settings| {
 2651        settings.defaults.tab_size = NonZeroU32::new(4)
 2652    });
 2653
 2654    let language = Arc::new(
 2655        Language::new(
 2656            LanguageConfig::default(),
 2657            Some(tree_sitter_rust::LANGUAGE.into()),
 2658        )
 2659        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2660        .unwrap(),
 2661    );
 2662
 2663    let mut cx = EditorTestContext::new(cx).await;
 2664    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2665    cx.set_state(indoc! {"
 2666        const a: ˇA = (
 2667 2668                «const_functionˇ»(ˇ),
 2669                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2670 2671        ˇ);ˇ
 2672    "});
 2673
 2674    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2675    cx.assert_editor_state(indoc! {"
 2676        ˇ
 2677        const a: A = (
 2678            ˇ
 2679            (
 2680                ˇ
 2681                ˇ
 2682                const_function(),
 2683                ˇ
 2684                ˇ
 2685                ˇ
 2686                ˇ
 2687                something_else,
 2688                ˇ
 2689            )
 2690            ˇ
 2691            ˇ
 2692        );
 2693    "});
 2694}
 2695
 2696#[gpui::test]
 2697async fn test_newline_below(cx: &mut TestAppContext) {
 2698    init_test(cx, |settings| {
 2699        settings.defaults.tab_size = NonZeroU32::new(4)
 2700    });
 2701
 2702    let language = Arc::new(
 2703        Language::new(
 2704            LanguageConfig::default(),
 2705            Some(tree_sitter_rust::LANGUAGE.into()),
 2706        )
 2707        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2708        .unwrap(),
 2709    );
 2710
 2711    let mut cx = EditorTestContext::new(cx).await;
 2712    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2713    cx.set_state(indoc! {"
 2714        const a: ˇA = (
 2715 2716                «const_functionˇ»(ˇ),
 2717                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2718 2719        ˇ);ˇ
 2720    "});
 2721
 2722    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2723    cx.assert_editor_state(indoc! {"
 2724        const a: A = (
 2725            ˇ
 2726            (
 2727                ˇ
 2728                const_function(),
 2729                ˇ
 2730                ˇ
 2731                something_else,
 2732                ˇ
 2733                ˇ
 2734                ˇ
 2735                ˇ
 2736            )
 2737            ˇ
 2738        );
 2739        ˇ
 2740        ˇ
 2741    "});
 2742}
 2743
 2744#[gpui::test]
 2745async fn test_newline_comments(cx: &mut TestAppContext) {
 2746    init_test(cx, |settings| {
 2747        settings.defaults.tab_size = NonZeroU32::new(4)
 2748    });
 2749
 2750    let language = Arc::new(Language::new(
 2751        LanguageConfig {
 2752            line_comments: vec!["//".into()],
 2753            ..LanguageConfig::default()
 2754        },
 2755        None,
 2756    ));
 2757    {
 2758        let mut cx = EditorTestContext::new(cx).await;
 2759        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2760        cx.set_state(indoc! {"
 2761        // Fooˇ
 2762    "});
 2763
 2764        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2765        cx.assert_editor_state(indoc! {"
 2766        // Foo
 2767        //ˇ
 2768    "});
 2769        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2770        cx.set_state(indoc! {"
 2771        ˇ// Foo
 2772    "});
 2773        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2774        cx.assert_editor_state(indoc! {"
 2775
 2776        ˇ// Foo
 2777    "});
 2778    }
 2779    // Ensure that comment continuations can be disabled.
 2780    update_test_language_settings(cx, |settings| {
 2781        settings.defaults.extend_comment_on_newline = Some(false);
 2782    });
 2783    let mut cx = EditorTestContext::new(cx).await;
 2784    cx.set_state(indoc! {"
 2785        // Fooˇ
 2786    "});
 2787    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2788    cx.assert_editor_state(indoc! {"
 2789        // Foo
 2790        ˇ
 2791    "});
 2792}
 2793
 2794#[gpui::test]
 2795fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2796    init_test(cx, |_| {});
 2797
 2798    let editor = cx.add_window(|window, cx| {
 2799        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2800        let mut editor = build_editor(buffer.clone(), window, cx);
 2801        editor.change_selections(None, window, cx, |s| {
 2802            s.select_ranges([3..4, 11..12, 19..20])
 2803        });
 2804        editor
 2805    });
 2806
 2807    _ = editor.update(cx, |editor, window, cx| {
 2808        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2809        editor.buffer.update(cx, |buffer, cx| {
 2810            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2811            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2812        });
 2813        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2814
 2815        editor.insert("Z", window, cx);
 2816        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2817
 2818        // The selections are moved after the inserted characters
 2819        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2820    });
 2821}
 2822
 2823#[gpui::test]
 2824async fn test_tab(cx: &mut TestAppContext) {
 2825    init_test(cx, |settings| {
 2826        settings.defaults.tab_size = NonZeroU32::new(3)
 2827    });
 2828
 2829    let mut cx = EditorTestContext::new(cx).await;
 2830    cx.set_state(indoc! {"
 2831        ˇabˇc
 2832        ˇ🏀ˇ🏀ˇefg
 2833 2834    "});
 2835    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2836    cx.assert_editor_state(indoc! {"
 2837           ˇab ˇc
 2838           ˇ🏀  ˇ🏀  ˇefg
 2839        d  ˇ
 2840    "});
 2841
 2842    cx.set_state(indoc! {"
 2843        a
 2844        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2845    "});
 2846    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2847    cx.assert_editor_state(indoc! {"
 2848        a
 2849           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851}
 2852
 2853#[gpui::test]
 2854async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2855    init_test(cx, |_| {});
 2856
 2857    let mut cx = EditorTestContext::new(cx).await;
 2858    let language = Arc::new(
 2859        Language::new(
 2860            LanguageConfig::default(),
 2861            Some(tree_sitter_rust::LANGUAGE.into()),
 2862        )
 2863        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2864        .unwrap(),
 2865    );
 2866    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2867
 2868    // cursors that are already at the suggested indent level insert
 2869    // a soft tab. cursors that are to the left of the suggested indent
 2870    // auto-indent their line.
 2871    cx.set_state(indoc! {"
 2872        ˇ
 2873        const a: B = (
 2874            c(
 2875                d(
 2876        ˇ
 2877                )
 2878        ˇ
 2879        ˇ    )
 2880        );
 2881    "});
 2882    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884            ˇ
 2885        const a: B = (
 2886            c(
 2887                d(
 2888                    ˇ
 2889                )
 2890                ˇ
 2891            ˇ)
 2892        );
 2893    "});
 2894
 2895    // handle auto-indent when there are multiple cursors on the same line
 2896    cx.set_state(indoc! {"
 2897        const a: B = (
 2898            c(
 2899        ˇ    ˇ
 2900        ˇ    )
 2901        );
 2902    "});
 2903    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2904    cx.assert_editor_state(indoc! {"
 2905        const a: B = (
 2906            c(
 2907                ˇ
 2908            ˇ)
 2909        );
 2910    "});
 2911}
 2912
 2913#[gpui::test]
 2914async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2915    init_test(cx, |settings| {
 2916        settings.defaults.tab_size = NonZeroU32::new(4)
 2917    });
 2918
 2919    let language = Arc::new(
 2920        Language::new(
 2921            LanguageConfig::default(),
 2922            Some(tree_sitter_rust::LANGUAGE.into()),
 2923        )
 2924        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2925        .unwrap(),
 2926    );
 2927
 2928    let mut cx = EditorTestContext::new(cx).await;
 2929    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2930    cx.set_state(indoc! {"
 2931        fn a() {
 2932            if b {
 2933        \t ˇc
 2934            }
 2935        }
 2936    "});
 2937
 2938    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2939    cx.assert_editor_state(indoc! {"
 2940        fn a() {
 2941            if b {
 2942                ˇc
 2943            }
 2944        }
 2945    "});
 2946}
 2947
 2948#[gpui::test]
 2949async fn test_indent_outdent(cx: &mut TestAppContext) {
 2950    init_test(cx, |settings| {
 2951        settings.defaults.tab_size = NonZeroU32::new(4);
 2952    });
 2953
 2954    let mut cx = EditorTestContext::new(cx).await;
 2955
 2956    cx.set_state(indoc! {"
 2957          «oneˇ» «twoˇ»
 2958        three
 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        three
 2965         four
 2966    "});
 2967
 2968    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        «oneˇ» «twoˇ»
 2971        three
 2972         four
 2973    "});
 2974
 2975    // select across line ending
 2976    cx.set_state(indoc! {"
 2977        one two
 2978        t«hree
 2979        ˇ» four
 2980    "});
 2981    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2982    cx.assert_editor_state(indoc! {"
 2983        one two
 2984            t«hree
 2985        ˇ» four
 2986    "});
 2987
 2988    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2989    cx.assert_editor_state(indoc! {"
 2990        one two
 2991        t«hree
 2992        ˇ» four
 2993    "});
 2994
 2995    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2996    cx.set_state(indoc! {"
 2997        one two
 2998        ˇthree
 2999            four
 3000    "});
 3001    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3002    cx.assert_editor_state(indoc! {"
 3003        one two
 3004            ˇthree
 3005            four
 3006    "});
 3007
 3008    cx.set_state(indoc! {"
 3009        one two
 3010        ˇ    three
 3011            four
 3012    "});
 3013    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3014    cx.assert_editor_state(indoc! {"
 3015        one two
 3016        ˇthree
 3017            four
 3018    "});
 3019}
 3020
 3021#[gpui::test]
 3022async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3023    init_test(cx, |settings| {
 3024        settings.defaults.hard_tabs = Some(true);
 3025    });
 3026
 3027    let mut cx = EditorTestContext::new(cx).await;
 3028
 3029    // select two ranges on one line
 3030    cx.set_state(indoc! {"
 3031        «oneˇ» «twoˇ»
 3032        three
 3033        four
 3034    "});
 3035    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3036    cx.assert_editor_state(indoc! {"
 3037        \t«oneˇ» «twoˇ»
 3038        three
 3039        four
 3040    "});
 3041    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3042    cx.assert_editor_state(indoc! {"
 3043        \t\t«oneˇ» «twoˇ»
 3044        three
 3045        four
 3046    "});
 3047    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3048    cx.assert_editor_state(indoc! {"
 3049        \t«oneˇ» «twoˇ»
 3050        three
 3051        four
 3052    "});
 3053    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3054    cx.assert_editor_state(indoc! {"
 3055        «oneˇ» «twoˇ»
 3056        three
 3057        four
 3058    "});
 3059
 3060    // select across a line ending
 3061    cx.set_state(indoc! {"
 3062        one two
 3063        t«hree
 3064        ˇ»four
 3065    "});
 3066    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3067    cx.assert_editor_state(indoc! {"
 3068        one two
 3069        \tt«hree
 3070        ˇ»four
 3071    "});
 3072    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3073    cx.assert_editor_state(indoc! {"
 3074        one two
 3075        \t\tt«hree
 3076        ˇ»four
 3077    "});
 3078    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3079    cx.assert_editor_state(indoc! {"
 3080        one two
 3081        \tt«hree
 3082        ˇ»four
 3083    "});
 3084    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3085    cx.assert_editor_state(indoc! {"
 3086        one two
 3087        t«hree
 3088        ˇ»four
 3089    "});
 3090
 3091    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3092    cx.set_state(indoc! {"
 3093        one two
 3094        ˇthree
 3095        four
 3096    "});
 3097    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        one two
 3100        ˇthree
 3101        four
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        one two
 3106        \tˇthree
 3107        four
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        one two
 3112        ˇthree
 3113        four
 3114    "});
 3115}
 3116
 3117#[gpui::test]
 3118fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3119    init_test(cx, |settings| {
 3120        settings.languages.extend([
 3121            (
 3122                "TOML".into(),
 3123                LanguageSettingsContent {
 3124                    tab_size: NonZeroU32::new(2),
 3125                    ..Default::default()
 3126                },
 3127            ),
 3128            (
 3129                "Rust".into(),
 3130                LanguageSettingsContent {
 3131                    tab_size: NonZeroU32::new(4),
 3132                    ..Default::default()
 3133                },
 3134            ),
 3135        ]);
 3136    });
 3137
 3138    let toml_language = Arc::new(Language::new(
 3139        LanguageConfig {
 3140            name: "TOML".into(),
 3141            ..Default::default()
 3142        },
 3143        None,
 3144    ));
 3145    let rust_language = Arc::new(Language::new(
 3146        LanguageConfig {
 3147            name: "Rust".into(),
 3148            ..Default::default()
 3149        },
 3150        None,
 3151    ));
 3152
 3153    let toml_buffer =
 3154        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3155    let rust_buffer =
 3156        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3157    let multibuffer = cx.new(|cx| {
 3158        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3159        multibuffer.push_excerpts(
 3160            toml_buffer.clone(),
 3161            [ExcerptRange {
 3162                context: Point::new(0, 0)..Point::new(2, 0),
 3163                primary: None,
 3164            }],
 3165            cx,
 3166        );
 3167        multibuffer.push_excerpts(
 3168            rust_buffer.clone(),
 3169            [ExcerptRange {
 3170                context: Point::new(0, 0)..Point::new(1, 0),
 3171                primary: None,
 3172            }],
 3173            cx,
 3174        );
 3175        multibuffer
 3176    });
 3177
 3178    cx.add_window(|window, cx| {
 3179        let mut editor = build_editor(multibuffer, window, cx);
 3180
 3181        assert_eq!(
 3182            editor.text(cx),
 3183            indoc! {"
 3184                a = 1
 3185                b = 2
 3186
 3187                const c: usize = 3;
 3188            "}
 3189        );
 3190
 3191        select_ranges(
 3192            &mut editor,
 3193            indoc! {"
 3194                «aˇ» = 1
 3195                b = 2
 3196
 3197                «const c:ˇ» usize = 3;
 3198            "},
 3199            window,
 3200            cx,
 3201        );
 3202
 3203        editor.tab(&Tab, window, cx);
 3204        assert_text_with_selections(
 3205            &mut editor,
 3206            indoc! {"
 3207                  «aˇ» = 1
 3208                b = 2
 3209
 3210                    «const c:ˇ» usize = 3;
 3211            "},
 3212            cx,
 3213        );
 3214        editor.backtab(&Backtab, window, cx);
 3215        assert_text_with_selections(
 3216            &mut editor,
 3217            indoc! {"
 3218                «aˇ» = 1
 3219                b = 2
 3220
 3221                «const c:ˇ» usize = 3;
 3222            "},
 3223            cx,
 3224        );
 3225
 3226        editor
 3227    });
 3228}
 3229
 3230#[gpui::test]
 3231async fn test_backspace(cx: &mut TestAppContext) {
 3232    init_test(cx, |_| {});
 3233
 3234    let mut cx = EditorTestContext::new(cx).await;
 3235
 3236    // Basic backspace
 3237    cx.set_state(indoc! {"
 3238        onˇe two three
 3239        fou«rˇ» five six
 3240        seven «ˇeight nine
 3241        »ten
 3242    "});
 3243    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3244    cx.assert_editor_state(indoc! {"
 3245        oˇe two three
 3246        fouˇ five six
 3247        seven ˇten
 3248    "});
 3249
 3250    // Test backspace inside and around indents
 3251    cx.set_state(indoc! {"
 3252        zero
 3253            ˇone
 3254                ˇtwo
 3255            ˇ ˇ ˇ  three
 3256        ˇ  ˇ  four
 3257    "});
 3258    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3259    cx.assert_editor_state(indoc! {"
 3260        zero
 3261        ˇone
 3262            ˇtwo
 3263        ˇ  threeˇ  four
 3264    "});
 3265
 3266    // Test backspace with line_mode set to true
 3267    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3268    cx.set_state(indoc! {"
 3269        The ˇquick ˇbrown
 3270        fox jumps over
 3271        the lazy dog
 3272        ˇThe qu«ick bˇ»rown"});
 3273    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3274    cx.assert_editor_state(indoc! {"
 3275        ˇfox jumps over
 3276        the lazy dogˇ"});
 3277}
 3278
 3279#[gpui::test]
 3280async fn test_delete(cx: &mut TestAppContext) {
 3281    init_test(cx, |_| {});
 3282
 3283    let mut cx = EditorTestContext::new(cx).await;
 3284    cx.set_state(indoc! {"
 3285        onˇe two three
 3286        fou«rˇ» five six
 3287        seven «ˇeight nine
 3288        »ten
 3289    "});
 3290    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3291    cx.assert_editor_state(indoc! {"
 3292        onˇ two three
 3293        fouˇ five six
 3294        seven ˇten
 3295    "});
 3296
 3297    // Test backspace with line_mode set to true
 3298    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3299    cx.set_state(indoc! {"
 3300        The ˇquick ˇbrown
 3301        fox «ˇjum»ps over
 3302        the lazy dog
 3303        ˇThe qu«ick bˇ»rown"});
 3304    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3305    cx.assert_editor_state("ˇthe lazy dogˇ");
 3306}
 3307
 3308#[gpui::test]
 3309fn test_delete_line(cx: &mut TestAppContext) {
 3310    init_test(cx, |_| {});
 3311
 3312    let editor = cx.add_window(|window, cx| {
 3313        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3314        build_editor(buffer, window, cx)
 3315    });
 3316    _ = editor.update(cx, |editor, window, cx| {
 3317        editor.change_selections(None, window, cx, |s| {
 3318            s.select_display_ranges([
 3319                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3320                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3321                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3322            ])
 3323        });
 3324        editor.delete_line(&DeleteLine, window, cx);
 3325        assert_eq!(editor.display_text(cx), "ghi");
 3326        assert_eq!(
 3327            editor.selections.display_ranges(cx),
 3328            vec![
 3329                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3330                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3331            ]
 3332        );
 3333    });
 3334
 3335    let editor = cx.add_window(|window, cx| {
 3336        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3337        build_editor(buffer, window, cx)
 3338    });
 3339    _ = editor.update(cx, |editor, window, cx| {
 3340        editor.change_selections(None, window, cx, |s| {
 3341            s.select_display_ranges([
 3342                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3343            ])
 3344        });
 3345        editor.delete_line(&DeleteLine, window, cx);
 3346        assert_eq!(editor.display_text(cx), "ghi\n");
 3347        assert_eq!(
 3348            editor.selections.display_ranges(cx),
 3349            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3350        );
 3351    });
 3352}
 3353
 3354#[gpui::test]
 3355fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3356    init_test(cx, |_| {});
 3357
 3358    cx.add_window(|window, cx| {
 3359        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3360        let mut editor = build_editor(buffer.clone(), window, cx);
 3361        let buffer = buffer.read(cx).as_singleton().unwrap();
 3362
 3363        assert_eq!(
 3364            editor.selections.ranges::<Point>(cx),
 3365            &[Point::new(0, 0)..Point::new(0, 0)]
 3366        );
 3367
 3368        // When on single line, replace newline at end by space
 3369        editor.join_lines(&JoinLines, window, cx);
 3370        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3371        assert_eq!(
 3372            editor.selections.ranges::<Point>(cx),
 3373            &[Point::new(0, 3)..Point::new(0, 3)]
 3374        );
 3375
 3376        // When multiple lines are selected, remove newlines that are spanned by the selection
 3377        editor.change_selections(None, window, cx, |s| {
 3378            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3379        });
 3380        editor.join_lines(&JoinLines, window, cx);
 3381        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3382        assert_eq!(
 3383            editor.selections.ranges::<Point>(cx),
 3384            &[Point::new(0, 11)..Point::new(0, 11)]
 3385        );
 3386
 3387        // Undo should be transactional
 3388        editor.undo(&Undo, window, cx);
 3389        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3390        assert_eq!(
 3391            editor.selections.ranges::<Point>(cx),
 3392            &[Point::new(0, 5)..Point::new(2, 2)]
 3393        );
 3394
 3395        // When joining an empty line don't insert a space
 3396        editor.change_selections(None, window, cx, |s| {
 3397            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3398        });
 3399        editor.join_lines(&JoinLines, window, cx);
 3400        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3401        assert_eq!(
 3402            editor.selections.ranges::<Point>(cx),
 3403            [Point::new(2, 3)..Point::new(2, 3)]
 3404        );
 3405
 3406        // We can remove trailing newlines
 3407        editor.join_lines(&JoinLines, window, cx);
 3408        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3409        assert_eq!(
 3410            editor.selections.ranges::<Point>(cx),
 3411            [Point::new(2, 3)..Point::new(2, 3)]
 3412        );
 3413
 3414        // We don't blow up on the last line
 3415        editor.join_lines(&JoinLines, window, cx);
 3416        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3417        assert_eq!(
 3418            editor.selections.ranges::<Point>(cx),
 3419            [Point::new(2, 3)..Point::new(2, 3)]
 3420        );
 3421
 3422        // reset to test indentation
 3423        editor.buffer.update(cx, |buffer, cx| {
 3424            buffer.edit(
 3425                [
 3426                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3427                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3428                ],
 3429                None,
 3430                cx,
 3431            )
 3432        });
 3433
 3434        // We remove any leading spaces
 3435        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3436        editor.change_selections(None, window, cx, |s| {
 3437            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3438        });
 3439        editor.join_lines(&JoinLines, window, cx);
 3440        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3441
 3442        // We don't insert a space for a line containing only spaces
 3443        editor.join_lines(&JoinLines, window, cx);
 3444        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3445
 3446        // We ignore any leading tabs
 3447        editor.join_lines(&JoinLines, window, cx);
 3448        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3449
 3450        editor
 3451    });
 3452}
 3453
 3454#[gpui::test]
 3455fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3456    init_test(cx, |_| {});
 3457
 3458    cx.add_window(|window, cx| {
 3459        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3460        let mut editor = build_editor(buffer.clone(), window, cx);
 3461        let buffer = buffer.read(cx).as_singleton().unwrap();
 3462
 3463        editor.change_selections(None, window, cx, |s| {
 3464            s.select_ranges([
 3465                Point::new(0, 2)..Point::new(1, 1),
 3466                Point::new(1, 2)..Point::new(1, 2),
 3467                Point::new(3, 1)..Point::new(3, 2),
 3468            ])
 3469        });
 3470
 3471        editor.join_lines(&JoinLines, window, cx);
 3472        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3473
 3474        assert_eq!(
 3475            editor.selections.ranges::<Point>(cx),
 3476            [
 3477                Point::new(0, 7)..Point::new(0, 7),
 3478                Point::new(1, 3)..Point::new(1, 3)
 3479            ]
 3480        );
 3481        editor
 3482    });
 3483}
 3484
 3485#[gpui::test]
 3486async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3487    init_test(cx, |_| {});
 3488
 3489    let mut cx = EditorTestContext::new(cx).await;
 3490
 3491    let diff_base = r#"
 3492        Line 0
 3493        Line 1
 3494        Line 2
 3495        Line 3
 3496        "#
 3497    .unindent();
 3498
 3499    cx.set_state(
 3500        &r#"
 3501        ˇLine 0
 3502        Line 1
 3503        Line 2
 3504        Line 3
 3505        "#
 3506        .unindent(),
 3507    );
 3508
 3509    cx.set_head_text(&diff_base);
 3510    executor.run_until_parked();
 3511
 3512    // Join lines
 3513    cx.update_editor(|editor, window, cx| {
 3514        editor.join_lines(&JoinLines, window, cx);
 3515    });
 3516    executor.run_until_parked();
 3517
 3518    cx.assert_editor_state(
 3519        &r#"
 3520        Line 0ˇ Line 1
 3521        Line 2
 3522        Line 3
 3523        "#
 3524        .unindent(),
 3525    );
 3526    // Join again
 3527    cx.update_editor(|editor, window, cx| {
 3528        editor.join_lines(&JoinLines, window, cx);
 3529    });
 3530    executor.run_until_parked();
 3531
 3532    cx.assert_editor_state(
 3533        &r#"
 3534        Line 0 Line 1ˇ Line 2
 3535        Line 3
 3536        "#
 3537        .unindent(),
 3538    );
 3539}
 3540
 3541#[gpui::test]
 3542async fn test_custom_newlines_cause_no_false_positive_diffs(
 3543    executor: BackgroundExecutor,
 3544    cx: &mut TestAppContext,
 3545) {
 3546    init_test(cx, |_| {});
 3547    let mut cx = EditorTestContext::new(cx).await;
 3548    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3549    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3550    executor.run_until_parked();
 3551
 3552    cx.update_editor(|editor, window, cx| {
 3553        let snapshot = editor.snapshot(window, cx);
 3554        assert_eq!(
 3555            snapshot
 3556                .buffer_snapshot
 3557                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3558                .collect::<Vec<_>>(),
 3559            Vec::new(),
 3560            "Should not have any diffs for files with custom newlines"
 3561        );
 3562    });
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    // Test sort_lines_case_insensitive()
 3572    cx.set_state(indoc! {"
 3573        «z
 3574        y
 3575        x
 3576        Z
 3577        Y
 3578        Xˇ»
 3579    "});
 3580    cx.update_editor(|e, window, cx| {
 3581        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3582    });
 3583    cx.assert_editor_state(indoc! {"
 3584        «x
 3585        X
 3586        y
 3587        Y
 3588        z
 3589        Zˇ»
 3590    "});
 3591
 3592    // Test reverse_lines()
 3593    cx.set_state(indoc! {"
 3594        «5
 3595        4
 3596        3
 3597        2
 3598        1ˇ»
 3599    "});
 3600    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3601    cx.assert_editor_state(indoc! {"
 3602        «1
 3603        2
 3604        3
 3605        4
 3606        5ˇ»
 3607    "});
 3608
 3609    // Skip testing shuffle_line()
 3610
 3611    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3612    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3613
 3614    // Don't manipulate when cursor is on single line, but expand the selection
 3615    cx.set_state(indoc! {"
 3616        ddˇdd
 3617        ccc
 3618        bb
 3619        a
 3620    "});
 3621    cx.update_editor(|e, window, cx| {
 3622        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3623    });
 3624    cx.assert_editor_state(indoc! {"
 3625        «ddddˇ»
 3626        ccc
 3627        bb
 3628        a
 3629    "});
 3630
 3631    // Basic manipulate case
 3632    // Start selection moves to column 0
 3633    // End of selection shrinks to fit shorter line
 3634    cx.set_state(indoc! {"
 3635        dd«d
 3636        ccc
 3637        bb
 3638        aaaaaˇ»
 3639    "});
 3640    cx.update_editor(|e, window, cx| {
 3641        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3642    });
 3643    cx.assert_editor_state(indoc! {"
 3644        «aaaaa
 3645        bb
 3646        ccc
 3647        dddˇ»
 3648    "});
 3649
 3650    // Manipulate case with newlines
 3651    cx.set_state(indoc! {"
 3652        dd«d
 3653        ccc
 3654
 3655        bb
 3656        aaaaa
 3657
 3658        ˇ»
 3659    "});
 3660    cx.update_editor(|e, window, cx| {
 3661        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3662    });
 3663    cx.assert_editor_state(indoc! {"
 3664        «
 3665
 3666        aaaaa
 3667        bb
 3668        ccc
 3669        dddˇ»
 3670
 3671    "});
 3672
 3673    // Adding new line
 3674    cx.set_state(indoc! {"
 3675        aa«a
 3676        bbˇ»b
 3677    "});
 3678    cx.update_editor(|e, window, cx| {
 3679        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3680    });
 3681    cx.assert_editor_state(indoc! {"
 3682        «aaa
 3683        bbb
 3684        added_lineˇ»
 3685    "});
 3686
 3687    // Removing line
 3688    cx.set_state(indoc! {"
 3689        aa«a
 3690        bbbˇ»
 3691    "});
 3692    cx.update_editor(|e, window, cx| {
 3693        e.manipulate_lines(window, cx, |lines| {
 3694            lines.pop();
 3695        })
 3696    });
 3697    cx.assert_editor_state(indoc! {"
 3698        «aaaˇ»
 3699    "});
 3700
 3701    // Removing all lines
 3702    cx.set_state(indoc! {"
 3703        aa«a
 3704        bbbˇ»
 3705    "});
 3706    cx.update_editor(|e, window, cx| {
 3707        e.manipulate_lines(window, cx, |lines| {
 3708            lines.drain(..);
 3709        })
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        ˇ
 3713    "});
 3714}
 3715
 3716#[gpui::test]
 3717async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3718    init_test(cx, |_| {});
 3719
 3720    let mut cx = EditorTestContext::new(cx).await;
 3721
 3722    // Consider continuous selection as single selection
 3723    cx.set_state(indoc! {"
 3724        Aaa«aa
 3725        cˇ»c«c
 3726        bb
 3727        aaaˇ»aa
 3728    "});
 3729    cx.update_editor(|e, window, cx| {
 3730        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3731    });
 3732    cx.assert_editor_state(indoc! {"
 3733        «Aaaaa
 3734        ccc
 3735        bb
 3736        aaaaaˇ»
 3737    "});
 3738
 3739    cx.set_state(indoc! {"
 3740        Aaa«aa
 3741        cˇ»c«c
 3742        bb
 3743        aaaˇ»aa
 3744    "});
 3745    cx.update_editor(|e, window, cx| {
 3746        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3747    });
 3748    cx.assert_editor_state(indoc! {"
 3749        «Aaaaa
 3750        ccc
 3751        bbˇ»
 3752    "});
 3753
 3754    // Consider non continuous selection as distinct dedup operations
 3755    cx.set_state(indoc! {"
 3756        «aaaaa
 3757        bb
 3758        aaaaa
 3759        aaaaaˇ»
 3760
 3761        aaa«aaˇ»
 3762    "});
 3763    cx.update_editor(|e, window, cx| {
 3764        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3765    });
 3766    cx.assert_editor_state(indoc! {"
 3767        «aaaaa
 3768        bbˇ»
 3769
 3770        «aaaaaˇ»
 3771    "});
 3772}
 3773
 3774#[gpui::test]
 3775async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3776    init_test(cx, |_| {});
 3777
 3778    let mut cx = EditorTestContext::new(cx).await;
 3779
 3780    cx.set_state(indoc! {"
 3781        «Aaa
 3782        aAa
 3783        Aaaˇ»
 3784    "});
 3785    cx.update_editor(|e, window, cx| {
 3786        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3787    });
 3788    cx.assert_editor_state(indoc! {"
 3789        «Aaa
 3790        aAaˇ»
 3791    "});
 3792
 3793    cx.set_state(indoc! {"
 3794        «Aaa
 3795        aAa
 3796        aaAˇ»
 3797    "});
 3798    cx.update_editor(|e, window, cx| {
 3799        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3800    });
 3801    cx.assert_editor_state(indoc! {"
 3802        «Aaaˇ»
 3803    "});
 3804}
 3805
 3806#[gpui::test]
 3807async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3808    init_test(cx, |_| {});
 3809
 3810    let mut cx = EditorTestContext::new(cx).await;
 3811
 3812    // Manipulate with multiple selections on a single line
 3813    cx.set_state(indoc! {"
 3814        dd«dd
 3815        cˇ»c«c
 3816        bb
 3817        aaaˇ»aa
 3818    "});
 3819    cx.update_editor(|e, window, cx| {
 3820        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3821    });
 3822    cx.assert_editor_state(indoc! {"
 3823        «aaaaa
 3824        bb
 3825        ccc
 3826        ddddˇ»
 3827    "});
 3828
 3829    // Manipulate with multiple disjoin selections
 3830    cx.set_state(indoc! {"
 3831 3832        4
 3833        3
 3834        2
 3835        1ˇ»
 3836
 3837        dd«dd
 3838        ccc
 3839        bb
 3840        aaaˇ»aa
 3841    "});
 3842    cx.update_editor(|e, window, cx| {
 3843        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3844    });
 3845    cx.assert_editor_state(indoc! {"
 3846        «1
 3847        2
 3848        3
 3849        4
 3850        5ˇ»
 3851
 3852        «aaaaa
 3853        bb
 3854        ccc
 3855        ddddˇ»
 3856    "});
 3857
 3858    // Adding lines on each selection
 3859    cx.set_state(indoc! {"
 3860 3861        1ˇ»
 3862
 3863        bb«bb
 3864        aaaˇ»aa
 3865    "});
 3866    cx.update_editor(|e, window, cx| {
 3867        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3868    });
 3869    cx.assert_editor_state(indoc! {"
 3870        «2
 3871        1
 3872        added lineˇ»
 3873
 3874        «bbbb
 3875        aaaaa
 3876        added lineˇ»
 3877    "});
 3878
 3879    // Removing lines on each selection
 3880    cx.set_state(indoc! {"
 3881 3882        1ˇ»
 3883
 3884        bb«bb
 3885        aaaˇ»aa
 3886    "});
 3887    cx.update_editor(|e, window, cx| {
 3888        e.manipulate_lines(window, cx, |lines| {
 3889            lines.pop();
 3890        })
 3891    });
 3892    cx.assert_editor_state(indoc! {"
 3893        «2ˇ»
 3894
 3895        «bbbbˇ»
 3896    "});
 3897}
 3898
 3899#[gpui::test]
 3900async fn test_manipulate_text(cx: &mut TestAppContext) {
 3901    init_test(cx, |_| {});
 3902
 3903    let mut cx = EditorTestContext::new(cx).await;
 3904
 3905    // Test convert_to_upper_case()
 3906    cx.set_state(indoc! {"
 3907        «hello worldˇ»
 3908    "});
 3909    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3910    cx.assert_editor_state(indoc! {"
 3911        «HELLO WORLDˇ»
 3912    "});
 3913
 3914    // Test convert_to_lower_case()
 3915    cx.set_state(indoc! {"
 3916        «HELLO WORLDˇ»
 3917    "});
 3918    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3919    cx.assert_editor_state(indoc! {"
 3920        «hello worldˇ»
 3921    "});
 3922
 3923    // Test multiple line, single selection case
 3924    cx.set_state(indoc! {"
 3925        «The quick brown
 3926        fox jumps over
 3927        the lazy dogˇ»
 3928    "});
 3929    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3930    cx.assert_editor_state(indoc! {"
 3931        «The Quick Brown
 3932        Fox Jumps Over
 3933        The Lazy Dogˇ»
 3934    "});
 3935
 3936    // Test multiple line, single selection case
 3937    cx.set_state(indoc! {"
 3938        «The quick brown
 3939        fox jumps over
 3940        the lazy dogˇ»
 3941    "});
 3942    cx.update_editor(|e, window, cx| {
 3943        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3944    });
 3945    cx.assert_editor_state(indoc! {"
 3946        «TheQuickBrown
 3947        FoxJumpsOver
 3948        TheLazyDogˇ»
 3949    "});
 3950
 3951    // From here on out, test more complex cases of manipulate_text()
 3952
 3953    // Test no selection case - should affect words cursors are in
 3954    // Cursor at beginning, middle, and end of word
 3955    cx.set_state(indoc! {"
 3956        ˇhello big beauˇtiful worldˇ
 3957    "});
 3958    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3959    cx.assert_editor_state(indoc! {"
 3960        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3961    "});
 3962
 3963    // Test multiple selections on a single line and across multiple lines
 3964    cx.set_state(indoc! {"
 3965        «Theˇ» quick «brown
 3966        foxˇ» jumps «overˇ»
 3967        the «lazyˇ» dog
 3968    "});
 3969    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3970    cx.assert_editor_state(indoc! {"
 3971        «THEˇ» quick «BROWN
 3972        FOXˇ» jumps «OVERˇ»
 3973        the «LAZYˇ» dog
 3974    "});
 3975
 3976    // Test case where text length grows
 3977    cx.set_state(indoc! {"
 3978        «tschüߡ»
 3979    "});
 3980    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3981    cx.assert_editor_state(indoc! {"
 3982        «TSCHÜSSˇ»
 3983    "});
 3984
 3985    // Test to make sure we don't crash when text shrinks
 3986    cx.set_state(indoc! {"
 3987        aaa_bbbˇ
 3988    "});
 3989    cx.update_editor(|e, window, cx| {
 3990        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3991    });
 3992    cx.assert_editor_state(indoc! {"
 3993        «aaaBbbˇ»
 3994    "});
 3995
 3996    // Test to make sure we all aware of the fact that each word can grow and shrink
 3997    // Final selections should be aware of this fact
 3998    cx.set_state(indoc! {"
 3999        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4000    "});
 4001    cx.update_editor(|e, window, cx| {
 4002        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4003    });
 4004    cx.assert_editor_state(indoc! {"
 4005        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4006    "});
 4007
 4008    cx.set_state(indoc! {"
 4009        «hElLo, WoRld!ˇ»
 4010    "});
 4011    cx.update_editor(|e, window, cx| {
 4012        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4013    });
 4014    cx.assert_editor_state(indoc! {"
 4015        «HeLlO, wOrLD!ˇ»
 4016    "});
 4017}
 4018
 4019#[gpui::test]
 4020fn test_duplicate_line(cx: &mut TestAppContext) {
 4021    init_test(cx, |_| {});
 4022
 4023    let editor = cx.add_window(|window, cx| {
 4024        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4025        build_editor(buffer, window, cx)
 4026    });
 4027    _ = editor.update(cx, |editor, window, cx| {
 4028        editor.change_selections(None, window, cx, |s| {
 4029            s.select_display_ranges([
 4030                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4031                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4032                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4033                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4034            ])
 4035        });
 4036        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4037        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4038        assert_eq!(
 4039            editor.selections.display_ranges(cx),
 4040            vec![
 4041                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4042                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4043                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4044                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4045            ]
 4046        );
 4047    });
 4048
 4049    let editor = cx.add_window(|window, cx| {
 4050        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4051        build_editor(buffer, window, cx)
 4052    });
 4053    _ = editor.update(cx, |editor, window, cx| {
 4054        editor.change_selections(None, window, cx, |s| {
 4055            s.select_display_ranges([
 4056                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4057                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4058            ])
 4059        });
 4060        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4061        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4062        assert_eq!(
 4063            editor.selections.display_ranges(cx),
 4064            vec![
 4065                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4066                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4067            ]
 4068        );
 4069    });
 4070
 4071    // With `move_upwards` the selections stay in place, except for
 4072    // the lines inserted above them
 4073    let editor = cx.add_window(|window, cx| {
 4074        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4075        build_editor(buffer, window, cx)
 4076    });
 4077    _ = editor.update(cx, |editor, window, cx| {
 4078        editor.change_selections(None, window, cx, |s| {
 4079            s.select_display_ranges([
 4080                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4081                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4082                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4083                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4084            ])
 4085        });
 4086        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4087        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4088        assert_eq!(
 4089            editor.selections.display_ranges(cx),
 4090            vec![
 4091                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4092                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4093                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4094                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4095            ]
 4096        );
 4097    });
 4098
 4099    let editor = cx.add_window(|window, cx| {
 4100        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4101        build_editor(buffer, window, cx)
 4102    });
 4103    _ = editor.update(cx, |editor, window, cx| {
 4104        editor.change_selections(None, window, cx, |s| {
 4105            s.select_display_ranges([
 4106                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4107                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4108            ])
 4109        });
 4110        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4111        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4112        assert_eq!(
 4113            editor.selections.display_ranges(cx),
 4114            vec![
 4115                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4116                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4117            ]
 4118        );
 4119    });
 4120
 4121    let editor = cx.add_window(|window, cx| {
 4122        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4123        build_editor(buffer, window, cx)
 4124    });
 4125    _ = editor.update(cx, |editor, window, cx| {
 4126        editor.change_selections(None, window, cx, |s| {
 4127            s.select_display_ranges([
 4128                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4129                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4130            ])
 4131        });
 4132        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4133        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4134        assert_eq!(
 4135            editor.selections.display_ranges(cx),
 4136            vec![
 4137                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4138                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4139            ]
 4140        );
 4141    });
 4142}
 4143
 4144#[gpui::test]
 4145fn test_move_line_up_down(cx: &mut TestAppContext) {
 4146    init_test(cx, |_| {});
 4147
 4148    let editor = cx.add_window(|window, cx| {
 4149        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4150        build_editor(buffer, window, cx)
 4151    });
 4152    _ = editor.update(cx, |editor, window, cx| {
 4153        editor.fold_creases(
 4154            vec![
 4155                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4156                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4157                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4158            ],
 4159            true,
 4160            window,
 4161            cx,
 4162        );
 4163        editor.change_selections(None, window, cx, |s| {
 4164            s.select_display_ranges([
 4165                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4166                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4167                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4168                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4169            ])
 4170        });
 4171        assert_eq!(
 4172            editor.display_text(cx),
 4173            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4174        );
 4175
 4176        editor.move_line_up(&MoveLineUp, window, cx);
 4177        assert_eq!(
 4178            editor.display_text(cx),
 4179            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4180        );
 4181        assert_eq!(
 4182            editor.selections.display_ranges(cx),
 4183            vec![
 4184                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4185                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4186                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4187                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4188            ]
 4189        );
 4190    });
 4191
 4192    _ = editor.update(cx, |editor, window, cx| {
 4193        editor.move_line_down(&MoveLineDown, window, cx);
 4194        assert_eq!(
 4195            editor.display_text(cx),
 4196            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4197        );
 4198        assert_eq!(
 4199            editor.selections.display_ranges(cx),
 4200            vec![
 4201                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4202                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4203                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4204                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4205            ]
 4206        );
 4207    });
 4208
 4209    _ = editor.update(cx, |editor, window, cx| {
 4210        editor.move_line_down(&MoveLineDown, window, cx);
 4211        assert_eq!(
 4212            editor.display_text(cx),
 4213            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4214        );
 4215        assert_eq!(
 4216            editor.selections.display_ranges(cx),
 4217            vec![
 4218                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4219                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4220                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4221                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4222            ]
 4223        );
 4224    });
 4225
 4226    _ = editor.update(cx, |editor, window, cx| {
 4227        editor.move_line_up(&MoveLineUp, window, cx);
 4228        assert_eq!(
 4229            editor.display_text(cx),
 4230            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4231        );
 4232        assert_eq!(
 4233            editor.selections.display_ranges(cx),
 4234            vec![
 4235                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4236                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4237                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4238                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4239            ]
 4240        );
 4241    });
 4242}
 4243
 4244#[gpui::test]
 4245fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4246    init_test(cx, |_| {});
 4247
 4248    let editor = cx.add_window(|window, cx| {
 4249        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4250        build_editor(buffer, window, cx)
 4251    });
 4252    _ = editor.update(cx, |editor, window, cx| {
 4253        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4254        editor.insert_blocks(
 4255            [BlockProperties {
 4256                style: BlockStyle::Fixed,
 4257                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4258                height: 1,
 4259                render: Arc::new(|_| div().into_any()),
 4260                priority: 0,
 4261            }],
 4262            Some(Autoscroll::fit()),
 4263            cx,
 4264        );
 4265        editor.change_selections(None, window, cx, |s| {
 4266            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4267        });
 4268        editor.move_line_down(&MoveLineDown, window, cx);
 4269    });
 4270}
 4271
 4272#[gpui::test]
 4273async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4274    init_test(cx, |_| {});
 4275
 4276    let mut cx = EditorTestContext::new(cx).await;
 4277    cx.set_state(
 4278        &"
 4279            ˇzero
 4280            one
 4281            two
 4282            three
 4283            four
 4284            five
 4285        "
 4286        .unindent(),
 4287    );
 4288
 4289    // Create a four-line block that replaces three lines of text.
 4290    cx.update_editor(|editor, window, cx| {
 4291        let snapshot = editor.snapshot(window, cx);
 4292        let snapshot = &snapshot.buffer_snapshot;
 4293        let placement = BlockPlacement::Replace(
 4294            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4295        );
 4296        editor.insert_blocks(
 4297            [BlockProperties {
 4298                placement,
 4299                height: 4,
 4300                style: BlockStyle::Sticky,
 4301                render: Arc::new(|_| gpui::div().into_any_element()),
 4302                priority: 0,
 4303            }],
 4304            None,
 4305            cx,
 4306        );
 4307    });
 4308
 4309    // Move down so that the cursor touches the block.
 4310    cx.update_editor(|editor, window, cx| {
 4311        editor.move_down(&Default::default(), window, cx);
 4312    });
 4313    cx.assert_editor_state(
 4314        &"
 4315            zero
 4316            «one
 4317            two
 4318            threeˇ»
 4319            four
 4320            five
 4321        "
 4322        .unindent(),
 4323    );
 4324
 4325    // Move down past the block.
 4326    cx.update_editor(|editor, window, cx| {
 4327        editor.move_down(&Default::default(), window, cx);
 4328    });
 4329    cx.assert_editor_state(
 4330        &"
 4331            zero
 4332            one
 4333            two
 4334            three
 4335            ˇfour
 4336            five
 4337        "
 4338        .unindent(),
 4339    );
 4340}
 4341
 4342#[gpui::test]
 4343fn test_transpose(cx: &mut TestAppContext) {
 4344    init_test(cx, |_| {});
 4345
 4346    _ = cx.add_window(|window, cx| {
 4347        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4348        editor.set_style(EditorStyle::default(), window, cx);
 4349        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4350        editor.transpose(&Default::default(), window, cx);
 4351        assert_eq!(editor.text(cx), "bac");
 4352        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4353
 4354        editor.transpose(&Default::default(), window, cx);
 4355        assert_eq!(editor.text(cx), "bca");
 4356        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4357
 4358        editor.transpose(&Default::default(), window, cx);
 4359        assert_eq!(editor.text(cx), "bac");
 4360        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4361
 4362        editor
 4363    });
 4364
 4365    _ = cx.add_window(|window, cx| {
 4366        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4367        editor.set_style(EditorStyle::default(), window, cx);
 4368        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4369        editor.transpose(&Default::default(), window, cx);
 4370        assert_eq!(editor.text(cx), "acb\nde");
 4371        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4372
 4373        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4374        editor.transpose(&Default::default(), window, cx);
 4375        assert_eq!(editor.text(cx), "acbd\ne");
 4376        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4377
 4378        editor.transpose(&Default::default(), window, cx);
 4379        assert_eq!(editor.text(cx), "acbde\n");
 4380        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4381
 4382        editor.transpose(&Default::default(), window, cx);
 4383        assert_eq!(editor.text(cx), "acbd\ne");
 4384        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4385
 4386        editor
 4387    });
 4388
 4389    _ = cx.add_window(|window, cx| {
 4390        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4391        editor.set_style(EditorStyle::default(), window, cx);
 4392        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4393        editor.transpose(&Default::default(), window, cx);
 4394        assert_eq!(editor.text(cx), "bacd\ne");
 4395        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4396
 4397        editor.transpose(&Default::default(), window, cx);
 4398        assert_eq!(editor.text(cx), "bcade\n");
 4399        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4400
 4401        editor.transpose(&Default::default(), window, cx);
 4402        assert_eq!(editor.text(cx), "bcda\ne");
 4403        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4404
 4405        editor.transpose(&Default::default(), window, cx);
 4406        assert_eq!(editor.text(cx), "bcade\n");
 4407        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4408
 4409        editor.transpose(&Default::default(), window, cx);
 4410        assert_eq!(editor.text(cx), "bcaed\n");
 4411        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4412
 4413        editor
 4414    });
 4415
 4416    _ = cx.add_window(|window, cx| {
 4417        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4418        editor.set_style(EditorStyle::default(), window, cx);
 4419        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4420        editor.transpose(&Default::default(), window, cx);
 4421        assert_eq!(editor.text(cx), "🏀🍐✋");
 4422        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4423
 4424        editor.transpose(&Default::default(), window, cx);
 4425        assert_eq!(editor.text(cx), "🏀✋🍐");
 4426        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4427
 4428        editor.transpose(&Default::default(), window, cx);
 4429        assert_eq!(editor.text(cx), "🏀🍐✋");
 4430        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4431
 4432        editor
 4433    });
 4434}
 4435
 4436#[gpui::test]
 4437async fn test_rewrap(cx: &mut TestAppContext) {
 4438    init_test(cx, |settings| {
 4439        settings.languages.extend([
 4440            (
 4441                "Markdown".into(),
 4442                LanguageSettingsContent {
 4443                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4444                    ..Default::default()
 4445                },
 4446            ),
 4447            (
 4448                "Plain Text".into(),
 4449                LanguageSettingsContent {
 4450                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4451                    ..Default::default()
 4452                },
 4453            ),
 4454        ])
 4455    });
 4456
 4457    let mut cx = EditorTestContext::new(cx).await;
 4458
 4459    let language_with_c_comments = Arc::new(Language::new(
 4460        LanguageConfig {
 4461            line_comments: vec!["// ".into()],
 4462            ..LanguageConfig::default()
 4463        },
 4464        None,
 4465    ));
 4466    let language_with_pound_comments = Arc::new(Language::new(
 4467        LanguageConfig {
 4468            line_comments: vec!["# ".into()],
 4469            ..LanguageConfig::default()
 4470        },
 4471        None,
 4472    ));
 4473    let markdown_language = Arc::new(Language::new(
 4474        LanguageConfig {
 4475            name: "Markdown".into(),
 4476            ..LanguageConfig::default()
 4477        },
 4478        None,
 4479    ));
 4480    let language_with_doc_comments = Arc::new(Language::new(
 4481        LanguageConfig {
 4482            line_comments: vec!["// ".into(), "/// ".into()],
 4483            ..LanguageConfig::default()
 4484        },
 4485        Some(tree_sitter_rust::LANGUAGE.into()),
 4486    ));
 4487
 4488    let plaintext_language = Arc::new(Language::new(
 4489        LanguageConfig {
 4490            name: "Plain Text".into(),
 4491            ..LanguageConfig::default()
 4492        },
 4493        None,
 4494    ));
 4495
 4496    assert_rewrap(
 4497        indoc! {"
 4498            // ˇ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.
 4499        "},
 4500        indoc! {"
 4501            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4502            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4503            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4504            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4505            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4506            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4507            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4508            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4509            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4510            // porttitor id. Aliquam id accumsan eros.
 4511        "},
 4512        language_with_c_comments.clone(),
 4513        &mut cx,
 4514    );
 4515
 4516    // Test that rewrapping works inside of a selection
 4517    assert_rewrap(
 4518        indoc! {"
 4519            «// 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.ˇ»
 4520        "},
 4521        indoc! {"
 4522            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4523            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4524            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4525            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4526            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4527            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4528            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4529            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4530            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4531            // porttitor id. Aliquam id accumsan eros.ˇ»
 4532        "},
 4533        language_with_c_comments.clone(),
 4534        &mut cx,
 4535    );
 4536
 4537    // Test that cursors that expand to the same region are collapsed.
 4538    assert_rewrap(
 4539        indoc! {"
 4540            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4541            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4542            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4543            // ˇ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.
 4544        "},
 4545        indoc! {"
 4546            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4547            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4548            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4549            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4550            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4551            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4552            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4553            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4554            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4555            // porttitor id. Aliquam id accumsan eros.
 4556        "},
 4557        language_with_c_comments.clone(),
 4558        &mut cx,
 4559    );
 4560
 4561    // Test that non-contiguous selections are treated separately.
 4562    assert_rewrap(
 4563        indoc! {"
 4564            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4565            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4566            //
 4567            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4568            // ˇ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.
 4569        "},
 4570        indoc! {"
 4571            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4572            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4573            // auctor, eu lacinia sapien scelerisque.
 4574            //
 4575            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4576            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4577            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4578            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4579            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4580            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4581            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4582        "},
 4583        language_with_c_comments.clone(),
 4584        &mut cx,
 4585    );
 4586
 4587    // Test that different comment prefixes are supported.
 4588    assert_rewrap(
 4589        indoc! {"
 4590            # ˇ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.
 4591        "},
 4592        indoc! {"
 4593            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4594            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4595            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4596            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4597            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4598            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4599            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4600            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4601            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4602            # accumsan eros.
 4603        "},
 4604        language_with_pound_comments.clone(),
 4605        &mut cx,
 4606    );
 4607
 4608    // Test that rewrapping is ignored outside of comments in most languages.
 4609    assert_rewrap(
 4610        indoc! {"
 4611            /// Adds two numbers.
 4612            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4613            fn add(a: u32, b: u32) -> u32 {
 4614                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ˇ
 4615            }
 4616        "},
 4617        indoc! {"
 4618            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4619            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4620            fn add(a: u32, b: u32) -> u32 {
 4621                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ˇ
 4622            }
 4623        "},
 4624        language_with_doc_comments.clone(),
 4625        &mut cx,
 4626    );
 4627
 4628    // Test that rewrapping works in Markdown and Plain Text languages.
 4629    assert_rewrap(
 4630        indoc! {"
 4631            # Hello
 4632
 4633            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.
 4634        "},
 4635        indoc! {"
 4636            # Hello
 4637
 4638            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4639            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4640            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4641            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4642            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4643            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4644            Integer sit amet scelerisque nisi.
 4645        "},
 4646        markdown_language,
 4647        &mut cx,
 4648    );
 4649
 4650    assert_rewrap(
 4651        indoc! {"
 4652            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.
 4653        "},
 4654        indoc! {"
 4655            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4656            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4657            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4658            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4659            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4660            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4661            Integer sit amet scelerisque nisi.
 4662        "},
 4663        plaintext_language,
 4664        &mut cx,
 4665    );
 4666
 4667    // Test rewrapping unaligned comments in a selection.
 4668    assert_rewrap(
 4669        indoc! {"
 4670            fn foo() {
 4671                if true {
 4672            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4673            // Praesent semper egestas tellus id dignissim.ˇ»
 4674                    do_something();
 4675                } else {
 4676                    //
 4677                }
 4678            }
 4679        "},
 4680        indoc! {"
 4681            fn foo() {
 4682                if true {
 4683            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4684                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4685                    // egestas tellus id dignissim.ˇ»
 4686                    do_something();
 4687                } else {
 4688                    //
 4689                }
 4690            }
 4691        "},
 4692        language_with_doc_comments.clone(),
 4693        &mut cx,
 4694    );
 4695
 4696    assert_rewrap(
 4697        indoc! {"
 4698            fn foo() {
 4699                if true {
 4700            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4701            // Praesent semper egestas tellus id dignissim.»
 4702                    do_something();
 4703                } else {
 4704                    //
 4705                }
 4706
 4707            }
 4708        "},
 4709        indoc! {"
 4710            fn foo() {
 4711                if true {
 4712            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4713                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4714                    // egestas tellus id dignissim.»
 4715                    do_something();
 4716                } else {
 4717                    //
 4718                }
 4719
 4720            }
 4721        "},
 4722        language_with_doc_comments.clone(),
 4723        &mut cx,
 4724    );
 4725
 4726    #[track_caller]
 4727    fn assert_rewrap(
 4728        unwrapped_text: &str,
 4729        wrapped_text: &str,
 4730        language: Arc<Language>,
 4731        cx: &mut EditorTestContext,
 4732    ) {
 4733        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4734        cx.set_state(unwrapped_text);
 4735        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4736        cx.assert_editor_state(wrapped_text);
 4737    }
 4738}
 4739
 4740#[gpui::test]
 4741async fn test_hard_wrap(cx: &mut TestAppContext) {
 4742    init_test(cx, |_| {});
 4743    let mut cx = EditorTestContext::new(cx).await;
 4744
 4745    cx.update_editor(|editor, _, cx| {
 4746        editor.set_hard_wrap(Some(14), cx);
 4747    });
 4748
 4749    cx.set_state(indoc!(
 4750        "
 4751        one two three ˇ
 4752        "
 4753    ));
 4754    cx.simulate_input("four");
 4755    cx.run_until_parked();
 4756
 4757    cx.assert_editor_state(indoc!(
 4758        "
 4759        one two three
 4760        fourˇ
 4761        "
 4762    ));
 4763}
 4764
 4765#[gpui::test]
 4766async fn test_clipboard(cx: &mut TestAppContext) {
 4767    init_test(cx, |_| {});
 4768
 4769    let mut cx = EditorTestContext::new(cx).await;
 4770
 4771    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4772    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4773    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4774
 4775    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4776    cx.set_state("two ˇfour ˇsix ˇ");
 4777    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4778    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4779
 4780    // Paste again but with only two cursors. Since the number of cursors doesn't
 4781    // match the number of slices in the clipboard, the entire clipboard text
 4782    // is pasted at each cursor.
 4783    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4784    cx.update_editor(|e, window, cx| {
 4785        e.handle_input("( ", window, cx);
 4786        e.paste(&Paste, window, cx);
 4787        e.handle_input(") ", window, cx);
 4788    });
 4789    cx.assert_editor_state(
 4790        &([
 4791            "( one✅ ",
 4792            "three ",
 4793            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4794            "three ",
 4795            "five ) ˇ",
 4796        ]
 4797        .join("\n")),
 4798    );
 4799
 4800    // Cut with three selections, one of which is full-line.
 4801    cx.set_state(indoc! {"
 4802        1«2ˇ»3
 4803        4ˇ567
 4804        «8ˇ»9"});
 4805    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4806    cx.assert_editor_state(indoc! {"
 4807        1ˇ3
 4808        ˇ9"});
 4809
 4810    // Paste with three selections, noticing how the copied selection that was full-line
 4811    // gets inserted before the second cursor.
 4812    cx.set_state(indoc! {"
 4813        1ˇ3
 4814 4815        «oˇ»ne"});
 4816    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4817    cx.assert_editor_state(indoc! {"
 4818        12ˇ3
 4819        4567
 4820 4821        8ˇne"});
 4822
 4823    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4824    cx.set_state(indoc! {"
 4825        The quick brown
 4826        fox juˇmps over
 4827        the lazy dog"});
 4828    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4829    assert_eq!(
 4830        cx.read_from_clipboard()
 4831            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4832        Some("fox jumps over\n".to_string())
 4833    );
 4834
 4835    // Paste with three selections, noticing how the copied full-line selection is inserted
 4836    // before the empty selections but replaces the selection that is non-empty.
 4837    cx.set_state(indoc! {"
 4838        Tˇhe quick brown
 4839        «foˇ»x jumps over
 4840        tˇhe lazy dog"});
 4841    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4842    cx.assert_editor_state(indoc! {"
 4843        fox jumps over
 4844        Tˇhe quick brown
 4845        fox jumps over
 4846        ˇx jumps over
 4847        fox jumps over
 4848        tˇhe lazy dog"});
 4849}
 4850
 4851#[gpui::test]
 4852async fn test_paste_multiline(cx: &mut TestAppContext) {
 4853    init_test(cx, |_| {});
 4854
 4855    let mut cx = EditorTestContext::new(cx).await;
 4856    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4857
 4858    // Cut an indented block, without the leading whitespace.
 4859    cx.set_state(indoc! {"
 4860        const a: B = (
 4861            c(),
 4862            «d(
 4863                e,
 4864                f
 4865            )ˇ»
 4866        );
 4867    "});
 4868    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4869    cx.assert_editor_state(indoc! {"
 4870        const a: B = (
 4871            c(),
 4872            ˇ
 4873        );
 4874    "});
 4875
 4876    // Paste it at the same position.
 4877    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4878    cx.assert_editor_state(indoc! {"
 4879        const a: B = (
 4880            c(),
 4881            d(
 4882                e,
 4883                f
 4884 4885        );
 4886    "});
 4887
 4888    // Paste it at a line with a lower indent level.
 4889    cx.set_state(indoc! {"
 4890        ˇ
 4891        const a: B = (
 4892            c(),
 4893        );
 4894    "});
 4895    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4896    cx.assert_editor_state(indoc! {"
 4897        d(
 4898            e,
 4899            f
 4900 4901        const a: B = (
 4902            c(),
 4903        );
 4904    "});
 4905
 4906    // Cut an indented block, with the leading whitespace.
 4907    cx.set_state(indoc! {"
 4908        const a: B = (
 4909            c(),
 4910        «    d(
 4911                e,
 4912                f
 4913            )
 4914        ˇ»);
 4915    "});
 4916    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4917    cx.assert_editor_state(indoc! {"
 4918        const a: B = (
 4919            c(),
 4920        ˇ);
 4921    "});
 4922
 4923    // Paste it at the same position.
 4924    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4925    cx.assert_editor_state(indoc! {"
 4926        const a: B = (
 4927            c(),
 4928            d(
 4929                e,
 4930                f
 4931            )
 4932        ˇ);
 4933    "});
 4934
 4935    // Paste it at a line with a higher indent level.
 4936    cx.set_state(indoc! {"
 4937        const a: B = (
 4938            c(),
 4939            d(
 4940                e,
 4941 4942            )
 4943        );
 4944    "});
 4945    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4946    cx.assert_editor_state(indoc! {"
 4947        const a: B = (
 4948            c(),
 4949            d(
 4950                e,
 4951                f    d(
 4952                    e,
 4953                    f
 4954                )
 4955        ˇ
 4956            )
 4957        );
 4958    "});
 4959
 4960    // Copy an indented block, starting mid-line
 4961    cx.set_state(indoc! {"
 4962        const a: B = (
 4963            c(),
 4964            somethin«g(
 4965                e,
 4966                f
 4967            )ˇ»
 4968        );
 4969    "});
 4970    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4971
 4972    // Paste it on a line with a lower indent level
 4973    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 4974    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4975    cx.assert_editor_state(indoc! {"
 4976        const a: B = (
 4977            c(),
 4978            something(
 4979                e,
 4980                f
 4981            )
 4982        );
 4983        g(
 4984            e,
 4985            f
 4986"});
 4987}
 4988
 4989#[gpui::test]
 4990async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4991    init_test(cx, |_| {});
 4992
 4993    cx.write_to_clipboard(ClipboardItem::new_string(
 4994        "    d(\n        e\n    );\n".into(),
 4995    ));
 4996
 4997    let mut cx = EditorTestContext::new(cx).await;
 4998    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4999
 5000    cx.set_state(indoc! {"
 5001        fn a() {
 5002            b();
 5003            if c() {
 5004                ˇ
 5005            }
 5006        }
 5007    "});
 5008
 5009    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5010    cx.assert_editor_state(indoc! {"
 5011        fn a() {
 5012            b();
 5013            if c() {
 5014                d(
 5015                    e
 5016                );
 5017        ˇ
 5018            }
 5019        }
 5020    "});
 5021
 5022    cx.set_state(indoc! {"
 5023        fn a() {
 5024            b();
 5025            ˇ
 5026        }
 5027    "});
 5028
 5029    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5030    cx.assert_editor_state(indoc! {"
 5031        fn a() {
 5032            b();
 5033            d(
 5034                e
 5035            );
 5036        ˇ
 5037        }
 5038    "});
 5039}
 5040
 5041#[gpui::test]
 5042fn test_select_all(cx: &mut TestAppContext) {
 5043    init_test(cx, |_| {});
 5044
 5045    let editor = cx.add_window(|window, cx| {
 5046        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5047        build_editor(buffer, window, cx)
 5048    });
 5049    _ = editor.update(cx, |editor, window, cx| {
 5050        editor.select_all(&SelectAll, window, cx);
 5051        assert_eq!(
 5052            editor.selections.display_ranges(cx),
 5053            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5054        );
 5055    });
 5056}
 5057
 5058#[gpui::test]
 5059fn test_select_line(cx: &mut TestAppContext) {
 5060    init_test(cx, |_| {});
 5061
 5062    let editor = cx.add_window(|window, cx| {
 5063        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5064        build_editor(buffer, window, cx)
 5065    });
 5066    _ = editor.update(cx, |editor, window, cx| {
 5067        editor.change_selections(None, window, cx, |s| {
 5068            s.select_display_ranges([
 5069                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5070                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5071                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5072                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5073            ])
 5074        });
 5075        editor.select_line(&SelectLine, window, cx);
 5076        assert_eq!(
 5077            editor.selections.display_ranges(cx),
 5078            vec![
 5079                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5080                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5081            ]
 5082        );
 5083    });
 5084
 5085    _ = editor.update(cx, |editor, window, cx| {
 5086        editor.select_line(&SelectLine, window, cx);
 5087        assert_eq!(
 5088            editor.selections.display_ranges(cx),
 5089            vec![
 5090                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5091                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5092            ]
 5093        );
 5094    });
 5095
 5096    _ = editor.update(cx, |editor, window, cx| {
 5097        editor.select_line(&SelectLine, window, cx);
 5098        assert_eq!(
 5099            editor.selections.display_ranges(cx),
 5100            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5101        );
 5102    });
 5103}
 5104
 5105#[gpui::test]
 5106async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5107    init_test(cx, |_| {});
 5108    let mut cx = EditorTestContext::new(cx).await;
 5109
 5110    #[track_caller]
 5111    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5112        cx.set_state(initial_state);
 5113        cx.update_editor(|e, window, cx| {
 5114            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5115        });
 5116        cx.assert_editor_state(expected_state);
 5117    }
 5118
 5119    // Selection starts and ends at the middle of lines, left-to-right
 5120    test(
 5121        &mut cx,
 5122        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5123        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5124    );
 5125    // Same thing, right-to-left
 5126    test(
 5127        &mut cx,
 5128        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5129        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5130    );
 5131
 5132    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5133    test(
 5134        &mut cx,
 5135        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5136        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5137    );
 5138    // Same thing, right-to-left
 5139    test(
 5140        &mut cx,
 5141        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5142        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5143    );
 5144
 5145    // Whole buffer, left-to-right, last line ends with newline
 5146    test(
 5147        &mut cx,
 5148        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5149        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5150    );
 5151    // Same thing, right-to-left
 5152    test(
 5153        &mut cx,
 5154        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5155        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5156    );
 5157
 5158    // Starts at the end of a line, ends at the start of another
 5159    test(
 5160        &mut cx,
 5161        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5162        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5163    );
 5164}
 5165
 5166#[gpui::test]
 5167async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5168    init_test(cx, |_| {});
 5169
 5170    let editor = cx.add_window(|window, cx| {
 5171        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5172        build_editor(buffer, window, cx)
 5173    });
 5174
 5175    // setup
 5176    _ = editor.update(cx, |editor, window, cx| {
 5177        editor.fold_creases(
 5178            vec![
 5179                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5180                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5181                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5182            ],
 5183            true,
 5184            window,
 5185            cx,
 5186        );
 5187        assert_eq!(
 5188            editor.display_text(cx),
 5189            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5190        );
 5191    });
 5192
 5193    _ = editor.update(cx, |editor, window, cx| {
 5194        editor.change_selections(None, window, cx, |s| {
 5195            s.select_display_ranges([
 5196                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5197                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5198                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5199                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5200            ])
 5201        });
 5202        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5203        assert_eq!(
 5204            editor.display_text(cx),
 5205            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5206        );
 5207    });
 5208    EditorTestContext::for_editor(editor, cx)
 5209        .await
 5210        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5211
 5212    _ = editor.update(cx, |editor, window, cx| {
 5213        editor.change_selections(None, window, cx, |s| {
 5214            s.select_display_ranges([
 5215                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5216            ])
 5217        });
 5218        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5219        assert_eq!(
 5220            editor.display_text(cx),
 5221            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5222        );
 5223        assert_eq!(
 5224            editor.selections.display_ranges(cx),
 5225            [
 5226                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5227                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5228                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5229                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5230                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5231                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5232                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5233            ]
 5234        );
 5235    });
 5236    EditorTestContext::for_editor(editor, cx)
 5237        .await
 5238        .assert_editor_state(
 5239            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5240        );
 5241}
 5242
 5243#[gpui::test]
 5244async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5245    init_test(cx, |_| {});
 5246
 5247    let mut cx = EditorTestContext::new(cx).await;
 5248
 5249    cx.set_state(indoc!(
 5250        r#"abc
 5251           defˇghi
 5252
 5253           jk
 5254           nlmo
 5255           "#
 5256    ));
 5257
 5258    cx.update_editor(|editor, window, cx| {
 5259        editor.add_selection_above(&Default::default(), window, cx);
 5260    });
 5261
 5262    cx.assert_editor_state(indoc!(
 5263        r#"abcˇ
 5264           defˇghi
 5265
 5266           jk
 5267           nlmo
 5268           "#
 5269    ));
 5270
 5271    cx.update_editor(|editor, window, cx| {
 5272        editor.add_selection_above(&Default::default(), window, cx);
 5273    });
 5274
 5275    cx.assert_editor_state(indoc!(
 5276        r#"abcˇ
 5277            defˇghi
 5278
 5279            jk
 5280            nlmo
 5281            "#
 5282    ));
 5283
 5284    cx.update_editor(|editor, window, cx| {
 5285        editor.add_selection_below(&Default::default(), window, cx);
 5286    });
 5287
 5288    cx.assert_editor_state(indoc!(
 5289        r#"abc
 5290           defˇghi
 5291
 5292           jk
 5293           nlmo
 5294           "#
 5295    ));
 5296
 5297    cx.update_editor(|editor, window, cx| {
 5298        editor.undo_selection(&Default::default(), window, cx);
 5299    });
 5300
 5301    cx.assert_editor_state(indoc!(
 5302        r#"abcˇ
 5303           defˇghi
 5304
 5305           jk
 5306           nlmo
 5307           "#
 5308    ));
 5309
 5310    cx.update_editor(|editor, window, cx| {
 5311        editor.redo_selection(&Default::default(), window, cx);
 5312    });
 5313
 5314    cx.assert_editor_state(indoc!(
 5315        r#"abc
 5316           defˇghi
 5317
 5318           jk
 5319           nlmo
 5320           "#
 5321    ));
 5322
 5323    cx.update_editor(|editor, window, cx| {
 5324        editor.add_selection_below(&Default::default(), window, cx);
 5325    });
 5326
 5327    cx.assert_editor_state(indoc!(
 5328        r#"abc
 5329           defˇghi
 5330
 5331           jk
 5332           nlmˇo
 5333           "#
 5334    ));
 5335
 5336    cx.update_editor(|editor, window, cx| {
 5337        editor.add_selection_below(&Default::default(), window, cx);
 5338    });
 5339
 5340    cx.assert_editor_state(indoc!(
 5341        r#"abc
 5342           defˇghi
 5343
 5344           jk
 5345           nlmˇo
 5346           "#
 5347    ));
 5348
 5349    // change selections
 5350    cx.set_state(indoc!(
 5351        r#"abc
 5352           def«ˇg»hi
 5353
 5354           jk
 5355           nlmo
 5356           "#
 5357    ));
 5358
 5359    cx.update_editor(|editor, window, cx| {
 5360        editor.add_selection_below(&Default::default(), window, cx);
 5361    });
 5362
 5363    cx.assert_editor_state(indoc!(
 5364        r#"abc
 5365           def«ˇg»hi
 5366
 5367           jk
 5368           nlm«ˇo»
 5369           "#
 5370    ));
 5371
 5372    cx.update_editor(|editor, window, cx| {
 5373        editor.add_selection_below(&Default::default(), window, cx);
 5374    });
 5375
 5376    cx.assert_editor_state(indoc!(
 5377        r#"abc
 5378           def«ˇg»hi
 5379
 5380           jk
 5381           nlm«ˇo»
 5382           "#
 5383    ));
 5384
 5385    cx.update_editor(|editor, window, cx| {
 5386        editor.add_selection_above(&Default::default(), window, cx);
 5387    });
 5388
 5389    cx.assert_editor_state(indoc!(
 5390        r#"abc
 5391           def«ˇg»hi
 5392
 5393           jk
 5394           nlmo
 5395           "#
 5396    ));
 5397
 5398    cx.update_editor(|editor, window, cx| {
 5399        editor.add_selection_above(&Default::default(), window, cx);
 5400    });
 5401
 5402    cx.assert_editor_state(indoc!(
 5403        r#"abc
 5404           def«ˇg»hi
 5405
 5406           jk
 5407           nlmo
 5408           "#
 5409    ));
 5410
 5411    // Change selections again
 5412    cx.set_state(indoc!(
 5413        r#"a«bc
 5414           defgˇ»hi
 5415
 5416           jk
 5417           nlmo
 5418           "#
 5419    ));
 5420
 5421    cx.update_editor(|editor, window, cx| {
 5422        editor.add_selection_below(&Default::default(), window, cx);
 5423    });
 5424
 5425    cx.assert_editor_state(indoc!(
 5426        r#"a«bcˇ»
 5427           d«efgˇ»hi
 5428
 5429           j«kˇ»
 5430           nlmo
 5431           "#
 5432    ));
 5433
 5434    cx.update_editor(|editor, window, cx| {
 5435        editor.add_selection_below(&Default::default(), window, cx);
 5436    });
 5437    cx.assert_editor_state(indoc!(
 5438        r#"a«bcˇ»
 5439           d«efgˇ»hi
 5440
 5441           j«kˇ»
 5442           n«lmoˇ»
 5443           "#
 5444    ));
 5445    cx.update_editor(|editor, window, cx| {
 5446        editor.add_selection_above(&Default::default(), window, cx);
 5447    });
 5448
 5449    cx.assert_editor_state(indoc!(
 5450        r#"a«bcˇ»
 5451           d«efgˇ»hi
 5452
 5453           j«kˇ»
 5454           nlmo
 5455           "#
 5456    ));
 5457
 5458    // Change selections again
 5459    cx.set_state(indoc!(
 5460        r#"abc
 5461           d«ˇefghi
 5462
 5463           jk
 5464           nlm»o
 5465           "#
 5466    ));
 5467
 5468    cx.update_editor(|editor, window, cx| {
 5469        editor.add_selection_above(&Default::default(), window, cx);
 5470    });
 5471
 5472    cx.assert_editor_state(indoc!(
 5473        r#"a«ˇbc»
 5474           d«ˇef»ghi
 5475
 5476           j«ˇk»
 5477           n«ˇlm»o
 5478           "#
 5479    ));
 5480
 5481    cx.update_editor(|editor, window, cx| {
 5482        editor.add_selection_below(&Default::default(), window, cx);
 5483    });
 5484
 5485    cx.assert_editor_state(indoc!(
 5486        r#"abc
 5487           d«ˇef»ghi
 5488
 5489           j«ˇk»
 5490           n«ˇlm»o
 5491           "#
 5492    ));
 5493}
 5494
 5495#[gpui::test]
 5496async fn test_select_next(cx: &mut TestAppContext) {
 5497    init_test(cx, |_| {});
 5498
 5499    let mut cx = EditorTestContext::new(cx).await;
 5500    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5501
 5502    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5503        .unwrap();
 5504    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5505
 5506    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5507        .unwrap();
 5508    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5509
 5510    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5511    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5512
 5513    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5514    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5515
 5516    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5517        .unwrap();
 5518    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5519
 5520    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5521        .unwrap();
 5522    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5523}
 5524
 5525#[gpui::test]
 5526async fn test_select_all_matches(cx: &mut TestAppContext) {
 5527    init_test(cx, |_| {});
 5528
 5529    let mut cx = EditorTestContext::new(cx).await;
 5530
 5531    // Test caret-only selections
 5532    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5533    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5534        .unwrap();
 5535    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5536
 5537    // Test left-to-right selections
 5538    cx.set_state("abc\n«abcˇ»\nabc");
 5539    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5540        .unwrap();
 5541    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5542
 5543    // Test right-to-left selections
 5544    cx.set_state("abc\n«ˇabc»\nabc");
 5545    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5546        .unwrap();
 5547    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5548
 5549    // Test selecting whitespace with caret selection
 5550    cx.set_state("abc\nˇ   abc\nabc");
 5551    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5552        .unwrap();
 5553    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5554
 5555    // Test selecting whitespace with left-to-right selection
 5556    cx.set_state("abc\n«ˇ  »abc\nabc");
 5557    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5558        .unwrap();
 5559    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5560
 5561    // Test no matches with right-to-left selection
 5562    cx.set_state("abc\n«  ˇ»abc\nabc");
 5563    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5564        .unwrap();
 5565    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5566}
 5567
 5568#[gpui::test]
 5569async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5570    init_test(cx, |_| {});
 5571
 5572    let mut cx = EditorTestContext::new(cx).await;
 5573    cx.set_state(
 5574        r#"let foo = 2;
 5575lˇet foo = 2;
 5576let fooˇ = 2;
 5577let foo = 2;
 5578let foo = ˇ2;"#,
 5579    );
 5580
 5581    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5582        .unwrap();
 5583    cx.assert_editor_state(
 5584        r#"let foo = 2;
 5585«letˇ» foo = 2;
 5586let «fooˇ» = 2;
 5587let foo = 2;
 5588let foo = «2ˇ»;"#,
 5589    );
 5590
 5591    // noop for multiple selections with different contents
 5592    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5593        .unwrap();
 5594    cx.assert_editor_state(
 5595        r#"let foo = 2;
 5596«letˇ» foo = 2;
 5597let «fooˇ» = 2;
 5598let foo = 2;
 5599let foo = «2ˇ»;"#,
 5600    );
 5601}
 5602
 5603#[gpui::test]
 5604async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5605    init_test(cx, |_| {});
 5606
 5607    let mut cx =
 5608        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5609
 5610    cx.assert_editor_state(indoc! {"
 5611        ˇbbb
 5612        ccc
 5613
 5614        bbb
 5615        ccc
 5616        "});
 5617    cx.dispatch_action(SelectPrevious::default());
 5618    cx.assert_editor_state(indoc! {"
 5619                «bbbˇ»
 5620                ccc
 5621
 5622                bbb
 5623                ccc
 5624                "});
 5625    cx.dispatch_action(SelectPrevious::default());
 5626    cx.assert_editor_state(indoc! {"
 5627                «bbbˇ»
 5628                ccc
 5629
 5630                «bbbˇ»
 5631                ccc
 5632                "});
 5633}
 5634
 5635#[gpui::test]
 5636async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5637    init_test(cx, |_| {});
 5638
 5639    let mut cx = EditorTestContext::new(cx).await;
 5640    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5641
 5642    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5643        .unwrap();
 5644    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5645
 5646    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5647        .unwrap();
 5648    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5649
 5650    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5651    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5652
 5653    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5654    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5655
 5656    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5657        .unwrap();
 5658    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5659
 5660    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5661        .unwrap();
 5662    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5663
 5664    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5665        .unwrap();
 5666    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5667}
 5668
 5669#[gpui::test]
 5670async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5671    init_test(cx, |_| {});
 5672
 5673    let mut cx = EditorTestContext::new(cx).await;
 5674    cx.set_state("");
 5675
 5676    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5677        .unwrap();
 5678    cx.assert_editor_state("«aˇ»");
 5679    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5680        .unwrap();
 5681    cx.assert_editor_state("«aˇ»");
 5682}
 5683
 5684#[gpui::test]
 5685async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5686    init_test(cx, |_| {});
 5687
 5688    let mut cx = EditorTestContext::new(cx).await;
 5689    cx.set_state(
 5690        r#"let foo = 2;
 5691lˇet foo = 2;
 5692let fooˇ = 2;
 5693let foo = 2;
 5694let foo = ˇ2;"#,
 5695    );
 5696
 5697    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5698        .unwrap();
 5699    cx.assert_editor_state(
 5700        r#"let foo = 2;
 5701«letˇ» foo = 2;
 5702let «fooˇ» = 2;
 5703let foo = 2;
 5704let foo = «2ˇ»;"#,
 5705    );
 5706
 5707    // noop for multiple selections with different contents
 5708    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5709        .unwrap();
 5710    cx.assert_editor_state(
 5711        r#"let foo = 2;
 5712«letˇ» foo = 2;
 5713let «fooˇ» = 2;
 5714let foo = 2;
 5715let foo = «2ˇ»;"#,
 5716    );
 5717}
 5718
 5719#[gpui::test]
 5720async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5721    init_test(cx, |_| {});
 5722
 5723    let mut cx = EditorTestContext::new(cx).await;
 5724    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5725
 5726    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5727        .unwrap();
 5728    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5729
 5730    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5731        .unwrap();
 5732    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5733
 5734    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5735    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5736
 5737    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5738    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5739
 5740    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5741        .unwrap();
 5742    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5743
 5744    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5745        .unwrap();
 5746    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5747}
 5748
 5749#[gpui::test]
 5750async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5751    init_test(cx, |_| {});
 5752
 5753    let language = Arc::new(Language::new(
 5754        LanguageConfig::default(),
 5755        Some(tree_sitter_rust::LANGUAGE.into()),
 5756    ));
 5757
 5758    let text = r#"
 5759        use mod1::mod2::{mod3, mod4};
 5760
 5761        fn fn_1(param1: bool, param2: &str) {
 5762            let var1 = "text";
 5763        }
 5764    "#
 5765    .unindent();
 5766
 5767    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5768    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5769    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5770
 5771    editor
 5772        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5773        .await;
 5774
 5775    editor.update_in(cx, |editor, window, cx| {
 5776        editor.change_selections(None, window, cx, |s| {
 5777            s.select_display_ranges([
 5778                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5779                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5780                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5781            ]);
 5782        });
 5783        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5784    });
 5785    editor.update(cx, |editor, cx| {
 5786        assert_text_with_selections(
 5787            editor,
 5788            indoc! {r#"
 5789                use mod1::mod2::{mod3, «mod4ˇ»};
 5790
 5791                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5792                    let var1 = "«textˇ»";
 5793                }
 5794            "#},
 5795            cx,
 5796        );
 5797    });
 5798
 5799    editor.update_in(cx, |editor, window, cx| {
 5800        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5801    });
 5802    editor.update(cx, |editor, cx| {
 5803        assert_text_with_selections(
 5804            editor,
 5805            indoc! {r#"
 5806                use mod1::mod2::«{mod3, mod4}ˇ»;
 5807
 5808                «ˇfn fn_1(param1: bool, param2: &str) {
 5809                    let var1 = "text";
 5810 5811            "#},
 5812            cx,
 5813        );
 5814    });
 5815
 5816    editor.update_in(cx, |editor, window, cx| {
 5817        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5818    });
 5819    assert_eq!(
 5820        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5821        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5822    );
 5823
 5824    // Trying to expand the selected syntax node one more time has no effect.
 5825    editor.update_in(cx, |editor, window, cx| {
 5826        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5827    });
 5828    assert_eq!(
 5829        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5830        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5831    );
 5832
 5833    editor.update_in(cx, |editor, window, cx| {
 5834        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5835    });
 5836    editor.update(cx, |editor, cx| {
 5837        assert_text_with_selections(
 5838            editor,
 5839            indoc! {r#"
 5840                use mod1::mod2::«{mod3, mod4}ˇ»;
 5841
 5842                «ˇfn fn_1(param1: bool, param2: &str) {
 5843                    let var1 = "text";
 5844 5845            "#},
 5846            cx,
 5847        );
 5848    });
 5849
 5850    editor.update_in(cx, |editor, window, cx| {
 5851        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5852    });
 5853    editor.update(cx, |editor, cx| {
 5854        assert_text_with_selections(
 5855            editor,
 5856            indoc! {r#"
 5857                use mod1::mod2::{mod3, «mod4ˇ»};
 5858
 5859                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5860                    let var1 = "«textˇ»";
 5861                }
 5862            "#},
 5863            cx,
 5864        );
 5865    });
 5866
 5867    editor.update_in(cx, |editor, window, cx| {
 5868        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5869    });
 5870    editor.update(cx, |editor, cx| {
 5871        assert_text_with_selections(
 5872            editor,
 5873            indoc! {r#"
 5874                use mod1::mod2::{mod3, mo«ˇ»d4};
 5875
 5876                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5877                    let var1 = "te«ˇ»xt";
 5878                }
 5879            "#},
 5880            cx,
 5881        );
 5882    });
 5883
 5884    // Trying to shrink the selected syntax node one more time has no effect.
 5885    editor.update_in(cx, |editor, window, cx| {
 5886        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5887    });
 5888    editor.update_in(cx, |editor, _, cx| {
 5889        assert_text_with_selections(
 5890            editor,
 5891            indoc! {r#"
 5892                use mod1::mod2::{mod3, mo«ˇ»d4};
 5893
 5894                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5895                    let var1 = "te«ˇ»xt";
 5896                }
 5897            "#},
 5898            cx,
 5899        );
 5900    });
 5901
 5902    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5903    // a fold.
 5904    editor.update_in(cx, |editor, window, cx| {
 5905        editor.fold_creases(
 5906            vec![
 5907                Crease::simple(
 5908                    Point::new(0, 21)..Point::new(0, 24),
 5909                    FoldPlaceholder::test(),
 5910                ),
 5911                Crease::simple(
 5912                    Point::new(3, 20)..Point::new(3, 22),
 5913                    FoldPlaceholder::test(),
 5914                ),
 5915            ],
 5916            true,
 5917            window,
 5918            cx,
 5919        );
 5920        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5921    });
 5922    editor.update(cx, |editor, cx| {
 5923        assert_text_with_selections(
 5924            editor,
 5925            indoc! {r#"
 5926                use mod1::mod2::«{mod3, mod4}ˇ»;
 5927
 5928                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5929                    «let var1 = "text";ˇ»
 5930                }
 5931            "#},
 5932            cx,
 5933        );
 5934    });
 5935}
 5936
 5937#[gpui::test]
 5938async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5939    init_test(cx, |_| {});
 5940
 5941    let base_text = r#"
 5942        impl A {
 5943            // this is an uncommitted comment
 5944
 5945            fn b() {
 5946                c();
 5947            }
 5948
 5949            // this is another uncommitted comment
 5950
 5951            fn d() {
 5952                // e
 5953                // f
 5954            }
 5955        }
 5956
 5957        fn g() {
 5958            // h
 5959        }
 5960    "#
 5961    .unindent();
 5962
 5963    let text = r#"
 5964        ˇimpl A {
 5965
 5966            fn b() {
 5967                c();
 5968            }
 5969
 5970            fn d() {
 5971                // e
 5972                // f
 5973            }
 5974        }
 5975
 5976        fn g() {
 5977            // h
 5978        }
 5979    "#
 5980    .unindent();
 5981
 5982    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5983    cx.set_state(&text);
 5984    cx.set_head_text(&base_text);
 5985    cx.update_editor(|editor, window, cx| {
 5986        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5987    });
 5988
 5989    cx.assert_state_with_diff(
 5990        "
 5991        ˇimpl A {
 5992      -     // this is an uncommitted comment
 5993
 5994            fn b() {
 5995                c();
 5996            }
 5997
 5998      -     // this is another uncommitted comment
 5999      -
 6000            fn d() {
 6001                // e
 6002                // f
 6003            }
 6004        }
 6005
 6006        fn g() {
 6007            // h
 6008        }
 6009    "
 6010        .unindent(),
 6011    );
 6012
 6013    let expected_display_text = "
 6014        impl A {
 6015            // this is an uncommitted comment
 6016
 6017            fn b() {
 6018 6019            }
 6020
 6021            // this is another uncommitted comment
 6022
 6023            fn d() {
 6024 6025            }
 6026        }
 6027
 6028        fn g() {
 6029 6030        }
 6031        "
 6032    .unindent();
 6033
 6034    cx.update_editor(|editor, window, cx| {
 6035        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6036        assert_eq!(editor.display_text(cx), expected_display_text);
 6037    });
 6038}
 6039
 6040#[gpui::test]
 6041async fn test_autoindent(cx: &mut TestAppContext) {
 6042    init_test(cx, |_| {});
 6043
 6044    let language = Arc::new(
 6045        Language::new(
 6046            LanguageConfig {
 6047                brackets: BracketPairConfig {
 6048                    pairs: vec![
 6049                        BracketPair {
 6050                            start: "{".to_string(),
 6051                            end: "}".to_string(),
 6052                            close: false,
 6053                            surround: false,
 6054                            newline: true,
 6055                        },
 6056                        BracketPair {
 6057                            start: "(".to_string(),
 6058                            end: ")".to_string(),
 6059                            close: false,
 6060                            surround: false,
 6061                            newline: true,
 6062                        },
 6063                    ],
 6064                    ..Default::default()
 6065                },
 6066                ..Default::default()
 6067            },
 6068            Some(tree_sitter_rust::LANGUAGE.into()),
 6069        )
 6070        .with_indents_query(
 6071            r#"
 6072                (_ "(" ")" @end) @indent
 6073                (_ "{" "}" @end) @indent
 6074            "#,
 6075        )
 6076        .unwrap(),
 6077    );
 6078
 6079    let text = "fn a() {}";
 6080
 6081    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6082    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6083    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6084    editor
 6085        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6086        .await;
 6087
 6088    editor.update_in(cx, |editor, window, cx| {
 6089        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6090        editor.newline(&Newline, window, cx);
 6091        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6092        assert_eq!(
 6093            editor.selections.ranges(cx),
 6094            &[
 6095                Point::new(1, 4)..Point::new(1, 4),
 6096                Point::new(3, 4)..Point::new(3, 4),
 6097                Point::new(5, 0)..Point::new(5, 0)
 6098            ]
 6099        );
 6100    });
 6101}
 6102
 6103#[gpui::test]
 6104async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6105    init_test(cx, |_| {});
 6106
 6107    {
 6108        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6109        cx.set_state(indoc! {"
 6110            impl A {
 6111
 6112                fn b() {}
 6113
 6114            «fn c() {
 6115
 6116            }ˇ»
 6117            }
 6118        "});
 6119
 6120        cx.update_editor(|editor, window, cx| {
 6121            editor.autoindent(&Default::default(), window, cx);
 6122        });
 6123
 6124        cx.assert_editor_state(indoc! {"
 6125            impl A {
 6126
 6127                fn b() {}
 6128
 6129                «fn c() {
 6130
 6131                }ˇ»
 6132            }
 6133        "});
 6134    }
 6135
 6136    {
 6137        let mut cx = EditorTestContext::new_multibuffer(
 6138            cx,
 6139            [indoc! { "
 6140                impl A {
 6141                «
 6142                // a
 6143                fn b(){}
 6144                »
 6145                «
 6146                    }
 6147                    fn c(){}
 6148                »
 6149            "}],
 6150        );
 6151
 6152        let buffer = cx.update_editor(|editor, _, cx| {
 6153            let buffer = editor.buffer().update(cx, |buffer, _| {
 6154                buffer.all_buffers().iter().next().unwrap().clone()
 6155            });
 6156            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6157            buffer
 6158        });
 6159
 6160        cx.run_until_parked();
 6161        cx.update_editor(|editor, window, cx| {
 6162            editor.select_all(&Default::default(), window, cx);
 6163            editor.autoindent(&Default::default(), window, cx)
 6164        });
 6165        cx.run_until_parked();
 6166
 6167        cx.update(|_, cx| {
 6168            pretty_assertions::assert_eq!(
 6169                buffer.read(cx).text(),
 6170                indoc! { "
 6171                    impl A {
 6172
 6173                        // a
 6174                        fn b(){}
 6175
 6176
 6177                    }
 6178                    fn c(){}
 6179
 6180                " }
 6181            )
 6182        });
 6183    }
 6184}
 6185
 6186#[gpui::test]
 6187async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6188    init_test(cx, |_| {});
 6189
 6190    let mut cx = EditorTestContext::new(cx).await;
 6191
 6192    let language = Arc::new(Language::new(
 6193        LanguageConfig {
 6194            brackets: BracketPairConfig {
 6195                pairs: vec![
 6196                    BracketPair {
 6197                        start: "{".to_string(),
 6198                        end: "}".to_string(),
 6199                        close: true,
 6200                        surround: true,
 6201                        newline: true,
 6202                    },
 6203                    BracketPair {
 6204                        start: "(".to_string(),
 6205                        end: ")".to_string(),
 6206                        close: true,
 6207                        surround: true,
 6208                        newline: true,
 6209                    },
 6210                    BracketPair {
 6211                        start: "/*".to_string(),
 6212                        end: " */".to_string(),
 6213                        close: true,
 6214                        surround: true,
 6215                        newline: true,
 6216                    },
 6217                    BracketPair {
 6218                        start: "[".to_string(),
 6219                        end: "]".to_string(),
 6220                        close: false,
 6221                        surround: false,
 6222                        newline: true,
 6223                    },
 6224                    BracketPair {
 6225                        start: "\"".to_string(),
 6226                        end: "\"".to_string(),
 6227                        close: true,
 6228                        surround: true,
 6229                        newline: false,
 6230                    },
 6231                    BracketPair {
 6232                        start: "<".to_string(),
 6233                        end: ">".to_string(),
 6234                        close: false,
 6235                        surround: true,
 6236                        newline: true,
 6237                    },
 6238                ],
 6239                ..Default::default()
 6240            },
 6241            autoclose_before: "})]".to_string(),
 6242            ..Default::default()
 6243        },
 6244        Some(tree_sitter_rust::LANGUAGE.into()),
 6245    ));
 6246
 6247    cx.language_registry().add(language.clone());
 6248    cx.update_buffer(|buffer, cx| {
 6249        buffer.set_language(Some(language), cx);
 6250    });
 6251
 6252    cx.set_state(
 6253        &r#"
 6254            🏀ˇ
 6255            εˇ
 6256            ❤️ˇ
 6257        "#
 6258        .unindent(),
 6259    );
 6260
 6261    // autoclose multiple nested brackets at multiple cursors
 6262    cx.update_editor(|editor, window, cx| {
 6263        editor.handle_input("{", window, cx);
 6264        editor.handle_input("{", window, cx);
 6265        editor.handle_input("{", window, cx);
 6266    });
 6267    cx.assert_editor_state(
 6268        &"
 6269            🏀{{{ˇ}}}
 6270            ε{{{ˇ}}}
 6271            ❤️{{{ˇ}}}
 6272        "
 6273        .unindent(),
 6274    );
 6275
 6276    // insert a different closing bracket
 6277    cx.update_editor(|editor, window, cx| {
 6278        editor.handle_input(")", window, cx);
 6279    });
 6280    cx.assert_editor_state(
 6281        &"
 6282            🏀{{{)ˇ}}}
 6283            ε{{{)ˇ}}}
 6284            ❤️{{{)ˇ}}}
 6285        "
 6286        .unindent(),
 6287    );
 6288
 6289    // skip over the auto-closed brackets when typing a closing bracket
 6290    cx.update_editor(|editor, window, cx| {
 6291        editor.move_right(&MoveRight, window, cx);
 6292        editor.handle_input("}", window, cx);
 6293        editor.handle_input("}", window, cx);
 6294        editor.handle_input("}", window, cx);
 6295    });
 6296    cx.assert_editor_state(
 6297        &"
 6298            🏀{{{)}}}}ˇ
 6299            ε{{{)}}}}ˇ
 6300            ❤️{{{)}}}}ˇ
 6301        "
 6302        .unindent(),
 6303    );
 6304
 6305    // autoclose multi-character pairs
 6306    cx.set_state(
 6307        &"
 6308            ˇ
 6309            ˇ
 6310        "
 6311        .unindent(),
 6312    );
 6313    cx.update_editor(|editor, window, cx| {
 6314        editor.handle_input("/", window, cx);
 6315        editor.handle_input("*", window, cx);
 6316    });
 6317    cx.assert_editor_state(
 6318        &"
 6319            /*ˇ */
 6320            /*ˇ */
 6321        "
 6322        .unindent(),
 6323    );
 6324
 6325    // one cursor autocloses a multi-character pair, one cursor
 6326    // does not autoclose.
 6327    cx.set_state(
 6328        &"
 6329 6330            ˇ
 6331        "
 6332        .unindent(),
 6333    );
 6334    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6335    cx.assert_editor_state(
 6336        &"
 6337            /*ˇ */
 6338 6339        "
 6340        .unindent(),
 6341    );
 6342
 6343    // Don't autoclose if the next character isn't whitespace and isn't
 6344    // listed in the language's "autoclose_before" section.
 6345    cx.set_state("ˇa b");
 6346    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6347    cx.assert_editor_state("{ˇa b");
 6348
 6349    // Don't autoclose if `close` is false for the bracket pair
 6350    cx.set_state("ˇ");
 6351    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6352    cx.assert_editor_state("");
 6353
 6354    // Surround with brackets if text is selected
 6355    cx.set_state("«aˇ» b");
 6356    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6357    cx.assert_editor_state("{«aˇ»} b");
 6358
 6359    // Autclose pair where the start and end characters are the same
 6360    cx.set_state("");
 6361    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6362    cx.assert_editor_state("a\"ˇ\"");
 6363    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6364    cx.assert_editor_state("a\"\"ˇ");
 6365
 6366    // Don't autoclose pair if autoclose is disabled
 6367    cx.set_state("ˇ");
 6368    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6369    cx.assert_editor_state("");
 6370
 6371    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6372    cx.set_state("«aˇ» b");
 6373    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6374    cx.assert_editor_state("<«aˇ»> b");
 6375}
 6376
 6377#[gpui::test]
 6378async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6379    init_test(cx, |settings| {
 6380        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6381    });
 6382
 6383    let mut cx = EditorTestContext::new(cx).await;
 6384
 6385    let language = Arc::new(Language::new(
 6386        LanguageConfig {
 6387            brackets: BracketPairConfig {
 6388                pairs: vec![
 6389                    BracketPair {
 6390                        start: "{".to_string(),
 6391                        end: "}".to_string(),
 6392                        close: true,
 6393                        surround: true,
 6394                        newline: true,
 6395                    },
 6396                    BracketPair {
 6397                        start: "(".to_string(),
 6398                        end: ")".to_string(),
 6399                        close: true,
 6400                        surround: true,
 6401                        newline: true,
 6402                    },
 6403                    BracketPair {
 6404                        start: "[".to_string(),
 6405                        end: "]".to_string(),
 6406                        close: false,
 6407                        surround: false,
 6408                        newline: true,
 6409                    },
 6410                ],
 6411                ..Default::default()
 6412            },
 6413            autoclose_before: "})]".to_string(),
 6414            ..Default::default()
 6415        },
 6416        Some(tree_sitter_rust::LANGUAGE.into()),
 6417    ));
 6418
 6419    cx.language_registry().add(language.clone());
 6420    cx.update_buffer(|buffer, cx| {
 6421        buffer.set_language(Some(language), cx);
 6422    });
 6423
 6424    cx.set_state(
 6425        &"
 6426            ˇ
 6427            ˇ
 6428            ˇ
 6429        "
 6430        .unindent(),
 6431    );
 6432
 6433    // ensure only matching closing brackets are skipped over
 6434    cx.update_editor(|editor, window, cx| {
 6435        editor.handle_input("}", window, cx);
 6436        editor.move_left(&MoveLeft, window, cx);
 6437        editor.handle_input(")", window, cx);
 6438        editor.move_left(&MoveLeft, window, cx);
 6439    });
 6440    cx.assert_editor_state(
 6441        &"
 6442            ˇ)}
 6443            ˇ)}
 6444            ˇ)}
 6445        "
 6446        .unindent(),
 6447    );
 6448
 6449    // skip-over closing brackets at multiple cursors
 6450    cx.update_editor(|editor, window, cx| {
 6451        editor.handle_input(")", window, cx);
 6452        editor.handle_input("}", window, cx);
 6453    });
 6454    cx.assert_editor_state(
 6455        &"
 6456            )}ˇ
 6457            )}ˇ
 6458            )}ˇ
 6459        "
 6460        .unindent(),
 6461    );
 6462
 6463    // ignore non-close brackets
 6464    cx.update_editor(|editor, window, cx| {
 6465        editor.handle_input("]", window, cx);
 6466        editor.move_left(&MoveLeft, window, cx);
 6467        editor.handle_input("]", window, cx);
 6468    });
 6469    cx.assert_editor_state(
 6470        &"
 6471            )}]ˇ]
 6472            )}]ˇ]
 6473            )}]ˇ]
 6474        "
 6475        .unindent(),
 6476    );
 6477}
 6478
 6479#[gpui::test]
 6480async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6481    init_test(cx, |_| {});
 6482
 6483    let mut cx = EditorTestContext::new(cx).await;
 6484
 6485    let html_language = Arc::new(
 6486        Language::new(
 6487            LanguageConfig {
 6488                name: "HTML".into(),
 6489                brackets: BracketPairConfig {
 6490                    pairs: vec![
 6491                        BracketPair {
 6492                            start: "<".into(),
 6493                            end: ">".into(),
 6494                            close: true,
 6495                            ..Default::default()
 6496                        },
 6497                        BracketPair {
 6498                            start: "{".into(),
 6499                            end: "}".into(),
 6500                            close: true,
 6501                            ..Default::default()
 6502                        },
 6503                        BracketPair {
 6504                            start: "(".into(),
 6505                            end: ")".into(),
 6506                            close: true,
 6507                            ..Default::default()
 6508                        },
 6509                    ],
 6510                    ..Default::default()
 6511                },
 6512                autoclose_before: "})]>".into(),
 6513                ..Default::default()
 6514            },
 6515            Some(tree_sitter_html::LANGUAGE.into()),
 6516        )
 6517        .with_injection_query(
 6518            r#"
 6519            (script_element
 6520                (raw_text) @injection.content
 6521                (#set! injection.language "javascript"))
 6522            "#,
 6523        )
 6524        .unwrap(),
 6525    );
 6526
 6527    let javascript_language = Arc::new(Language::new(
 6528        LanguageConfig {
 6529            name: "JavaScript".into(),
 6530            brackets: BracketPairConfig {
 6531                pairs: vec![
 6532                    BracketPair {
 6533                        start: "/*".into(),
 6534                        end: " */".into(),
 6535                        close: true,
 6536                        ..Default::default()
 6537                    },
 6538                    BracketPair {
 6539                        start: "{".into(),
 6540                        end: "}".into(),
 6541                        close: true,
 6542                        ..Default::default()
 6543                    },
 6544                    BracketPair {
 6545                        start: "(".into(),
 6546                        end: ")".into(),
 6547                        close: true,
 6548                        ..Default::default()
 6549                    },
 6550                ],
 6551                ..Default::default()
 6552            },
 6553            autoclose_before: "})]>".into(),
 6554            ..Default::default()
 6555        },
 6556        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6557    ));
 6558
 6559    cx.language_registry().add(html_language.clone());
 6560    cx.language_registry().add(javascript_language.clone());
 6561
 6562    cx.update_buffer(|buffer, cx| {
 6563        buffer.set_language(Some(html_language), cx);
 6564    });
 6565
 6566    cx.set_state(
 6567        &r#"
 6568            <body>ˇ
 6569                <script>
 6570                    var x = 1;ˇ
 6571                </script>
 6572            </body>ˇ
 6573        "#
 6574        .unindent(),
 6575    );
 6576
 6577    // Precondition: different languages are active at different locations.
 6578    cx.update_editor(|editor, window, cx| {
 6579        let snapshot = editor.snapshot(window, cx);
 6580        let cursors = editor.selections.ranges::<usize>(cx);
 6581        let languages = cursors
 6582            .iter()
 6583            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6584            .collect::<Vec<_>>();
 6585        assert_eq!(
 6586            languages,
 6587            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6588        );
 6589    });
 6590
 6591    // Angle brackets autoclose in HTML, but not JavaScript.
 6592    cx.update_editor(|editor, window, cx| {
 6593        editor.handle_input("<", window, cx);
 6594        editor.handle_input("a", window, cx);
 6595    });
 6596    cx.assert_editor_state(
 6597        &r#"
 6598            <body><aˇ>
 6599                <script>
 6600                    var x = 1;<aˇ
 6601                </script>
 6602            </body><aˇ>
 6603        "#
 6604        .unindent(),
 6605    );
 6606
 6607    // Curly braces and parens autoclose in both HTML and JavaScript.
 6608    cx.update_editor(|editor, window, cx| {
 6609        editor.handle_input(" b=", window, cx);
 6610        editor.handle_input("{", window, cx);
 6611        editor.handle_input("c", window, cx);
 6612        editor.handle_input("(", window, cx);
 6613    });
 6614    cx.assert_editor_state(
 6615        &r#"
 6616            <body><a b={c(ˇ)}>
 6617                <script>
 6618                    var x = 1;<a b={c(ˇ)}
 6619                </script>
 6620            </body><a b={c(ˇ)}>
 6621        "#
 6622        .unindent(),
 6623    );
 6624
 6625    // Brackets that were already autoclosed are skipped.
 6626    cx.update_editor(|editor, window, cx| {
 6627        editor.handle_input(")", window, cx);
 6628        editor.handle_input("d", window, cx);
 6629        editor.handle_input("}", window, cx);
 6630    });
 6631    cx.assert_editor_state(
 6632        &r#"
 6633            <body><a b={c()d}ˇ>
 6634                <script>
 6635                    var x = 1;<a b={c()d}ˇ
 6636                </script>
 6637            </body><a b={c()d}ˇ>
 6638        "#
 6639        .unindent(),
 6640    );
 6641    cx.update_editor(|editor, window, cx| {
 6642        editor.handle_input(">", window, cx);
 6643    });
 6644    cx.assert_editor_state(
 6645        &r#"
 6646            <body><a b={c()d}>ˇ
 6647                <script>
 6648                    var x = 1;<a b={c()d}>ˇ
 6649                </script>
 6650            </body><a b={c()d}>ˇ
 6651        "#
 6652        .unindent(),
 6653    );
 6654
 6655    // Reset
 6656    cx.set_state(
 6657        &r#"
 6658            <body>ˇ
 6659                <script>
 6660                    var x = 1;ˇ
 6661                </script>
 6662            </body>ˇ
 6663        "#
 6664        .unindent(),
 6665    );
 6666
 6667    cx.update_editor(|editor, window, cx| {
 6668        editor.handle_input("<", window, cx);
 6669    });
 6670    cx.assert_editor_state(
 6671        &r#"
 6672            <body><ˇ>
 6673                <script>
 6674                    var x = 1;<ˇ
 6675                </script>
 6676            </body><ˇ>
 6677        "#
 6678        .unindent(),
 6679    );
 6680
 6681    // When backspacing, the closing angle brackets are removed.
 6682    cx.update_editor(|editor, window, cx| {
 6683        editor.backspace(&Backspace, window, cx);
 6684    });
 6685    cx.assert_editor_state(
 6686        &r#"
 6687            <body>ˇ
 6688                <script>
 6689                    var x = 1;ˇ
 6690                </script>
 6691            </body>ˇ
 6692        "#
 6693        .unindent(),
 6694    );
 6695
 6696    // Block comments autoclose in JavaScript, but not HTML.
 6697    cx.update_editor(|editor, window, cx| {
 6698        editor.handle_input("/", window, cx);
 6699        editor.handle_input("*", window, cx);
 6700    });
 6701    cx.assert_editor_state(
 6702        &r#"
 6703            <body>/*ˇ
 6704                <script>
 6705                    var x = 1;/*ˇ */
 6706                </script>
 6707            </body>/*ˇ
 6708        "#
 6709        .unindent(),
 6710    );
 6711}
 6712
 6713#[gpui::test]
 6714async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6715    init_test(cx, |_| {});
 6716
 6717    let mut cx = EditorTestContext::new(cx).await;
 6718
 6719    let rust_language = Arc::new(
 6720        Language::new(
 6721            LanguageConfig {
 6722                name: "Rust".into(),
 6723                brackets: serde_json::from_value(json!([
 6724                    { "start": "{", "end": "}", "close": true, "newline": true },
 6725                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6726                ]))
 6727                .unwrap(),
 6728                autoclose_before: "})]>".into(),
 6729                ..Default::default()
 6730            },
 6731            Some(tree_sitter_rust::LANGUAGE.into()),
 6732        )
 6733        .with_override_query("(string_literal) @string")
 6734        .unwrap(),
 6735    );
 6736
 6737    cx.language_registry().add(rust_language.clone());
 6738    cx.update_buffer(|buffer, cx| {
 6739        buffer.set_language(Some(rust_language), cx);
 6740    });
 6741
 6742    cx.set_state(
 6743        &r#"
 6744            let x = ˇ
 6745        "#
 6746        .unindent(),
 6747    );
 6748
 6749    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6750    cx.update_editor(|editor, window, cx| {
 6751        editor.handle_input("\"", window, cx);
 6752    });
 6753    cx.assert_editor_state(
 6754        &r#"
 6755            let x = "ˇ"
 6756        "#
 6757        .unindent(),
 6758    );
 6759
 6760    // Inserting another quotation mark. The cursor moves across the existing
 6761    // automatically-inserted quotation mark.
 6762    cx.update_editor(|editor, window, cx| {
 6763        editor.handle_input("\"", window, cx);
 6764    });
 6765    cx.assert_editor_state(
 6766        &r#"
 6767            let x = ""ˇ
 6768        "#
 6769        .unindent(),
 6770    );
 6771
 6772    // Reset
 6773    cx.set_state(
 6774        &r#"
 6775            let x = ˇ
 6776        "#
 6777        .unindent(),
 6778    );
 6779
 6780    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6781    cx.update_editor(|editor, window, cx| {
 6782        editor.handle_input("\"", window, cx);
 6783        editor.handle_input(" ", window, cx);
 6784        editor.move_left(&Default::default(), window, cx);
 6785        editor.handle_input("\\", window, cx);
 6786        editor.handle_input("\"", window, cx);
 6787    });
 6788    cx.assert_editor_state(
 6789        &r#"
 6790            let x = "\"ˇ "
 6791        "#
 6792        .unindent(),
 6793    );
 6794
 6795    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6796    // mark. Nothing is inserted.
 6797    cx.update_editor(|editor, window, cx| {
 6798        editor.move_right(&Default::default(), window, cx);
 6799        editor.handle_input("\"", window, cx);
 6800    });
 6801    cx.assert_editor_state(
 6802        &r#"
 6803            let x = "\" "ˇ
 6804        "#
 6805        .unindent(),
 6806    );
 6807}
 6808
 6809#[gpui::test]
 6810async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6811    init_test(cx, |_| {});
 6812
 6813    let language = Arc::new(Language::new(
 6814        LanguageConfig {
 6815            brackets: BracketPairConfig {
 6816                pairs: vec![
 6817                    BracketPair {
 6818                        start: "{".to_string(),
 6819                        end: "}".to_string(),
 6820                        close: true,
 6821                        surround: true,
 6822                        newline: true,
 6823                    },
 6824                    BracketPair {
 6825                        start: "/* ".to_string(),
 6826                        end: "*/".to_string(),
 6827                        close: true,
 6828                        surround: true,
 6829                        ..Default::default()
 6830                    },
 6831                ],
 6832                ..Default::default()
 6833            },
 6834            ..Default::default()
 6835        },
 6836        Some(tree_sitter_rust::LANGUAGE.into()),
 6837    ));
 6838
 6839    let text = r#"
 6840        a
 6841        b
 6842        c
 6843    "#
 6844    .unindent();
 6845
 6846    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6847    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6848    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6849    editor
 6850        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6851        .await;
 6852
 6853    editor.update_in(cx, |editor, window, cx| {
 6854        editor.change_selections(None, window, cx, |s| {
 6855            s.select_display_ranges([
 6856                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6857                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6858                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6859            ])
 6860        });
 6861
 6862        editor.handle_input("{", window, cx);
 6863        editor.handle_input("{", window, cx);
 6864        editor.handle_input("{", window, cx);
 6865        assert_eq!(
 6866            editor.text(cx),
 6867            "
 6868                {{{a}}}
 6869                {{{b}}}
 6870                {{{c}}}
 6871            "
 6872            .unindent()
 6873        );
 6874        assert_eq!(
 6875            editor.selections.display_ranges(cx),
 6876            [
 6877                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6878                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6879                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6880            ]
 6881        );
 6882
 6883        editor.undo(&Undo, window, cx);
 6884        editor.undo(&Undo, window, cx);
 6885        editor.undo(&Undo, window, cx);
 6886        assert_eq!(
 6887            editor.text(cx),
 6888            "
 6889                a
 6890                b
 6891                c
 6892            "
 6893            .unindent()
 6894        );
 6895        assert_eq!(
 6896            editor.selections.display_ranges(cx),
 6897            [
 6898                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6899                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6900                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6901            ]
 6902        );
 6903
 6904        // Ensure inserting the first character of a multi-byte bracket pair
 6905        // doesn't surround the selections with the bracket.
 6906        editor.handle_input("/", window, cx);
 6907        assert_eq!(
 6908            editor.text(cx),
 6909            "
 6910                /
 6911                /
 6912                /
 6913            "
 6914            .unindent()
 6915        );
 6916        assert_eq!(
 6917            editor.selections.display_ranges(cx),
 6918            [
 6919                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6920                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6921                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6922            ]
 6923        );
 6924
 6925        editor.undo(&Undo, window, cx);
 6926        assert_eq!(
 6927            editor.text(cx),
 6928            "
 6929                a
 6930                b
 6931                c
 6932            "
 6933            .unindent()
 6934        );
 6935        assert_eq!(
 6936            editor.selections.display_ranges(cx),
 6937            [
 6938                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6939                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6940                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6941            ]
 6942        );
 6943
 6944        // Ensure inserting the last character of a multi-byte bracket pair
 6945        // doesn't surround the selections with the bracket.
 6946        editor.handle_input("*", window, cx);
 6947        assert_eq!(
 6948            editor.text(cx),
 6949            "
 6950                *
 6951                *
 6952                *
 6953            "
 6954            .unindent()
 6955        );
 6956        assert_eq!(
 6957            editor.selections.display_ranges(cx),
 6958            [
 6959                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6960                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6961                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6962            ]
 6963        );
 6964    });
 6965}
 6966
 6967#[gpui::test]
 6968async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6969    init_test(cx, |_| {});
 6970
 6971    let language = Arc::new(Language::new(
 6972        LanguageConfig {
 6973            brackets: BracketPairConfig {
 6974                pairs: vec![BracketPair {
 6975                    start: "{".to_string(),
 6976                    end: "}".to_string(),
 6977                    close: true,
 6978                    surround: true,
 6979                    newline: true,
 6980                }],
 6981                ..Default::default()
 6982            },
 6983            autoclose_before: "}".to_string(),
 6984            ..Default::default()
 6985        },
 6986        Some(tree_sitter_rust::LANGUAGE.into()),
 6987    ));
 6988
 6989    let text = r#"
 6990        a
 6991        b
 6992        c
 6993    "#
 6994    .unindent();
 6995
 6996    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6997    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6998    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6999    editor
 7000        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7001        .await;
 7002
 7003    editor.update_in(cx, |editor, window, cx| {
 7004        editor.change_selections(None, window, cx, |s| {
 7005            s.select_ranges([
 7006                Point::new(0, 1)..Point::new(0, 1),
 7007                Point::new(1, 1)..Point::new(1, 1),
 7008                Point::new(2, 1)..Point::new(2, 1),
 7009            ])
 7010        });
 7011
 7012        editor.handle_input("{", window, cx);
 7013        editor.handle_input("{", window, cx);
 7014        editor.handle_input("_", window, cx);
 7015        assert_eq!(
 7016            editor.text(cx),
 7017            "
 7018                a{{_}}
 7019                b{{_}}
 7020                c{{_}}
 7021            "
 7022            .unindent()
 7023        );
 7024        assert_eq!(
 7025            editor.selections.ranges::<Point>(cx),
 7026            [
 7027                Point::new(0, 4)..Point::new(0, 4),
 7028                Point::new(1, 4)..Point::new(1, 4),
 7029                Point::new(2, 4)..Point::new(2, 4)
 7030            ]
 7031        );
 7032
 7033        editor.backspace(&Default::default(), window, cx);
 7034        editor.backspace(&Default::default(), window, cx);
 7035        assert_eq!(
 7036            editor.text(cx),
 7037            "
 7038                a{}
 7039                b{}
 7040                c{}
 7041            "
 7042            .unindent()
 7043        );
 7044        assert_eq!(
 7045            editor.selections.ranges::<Point>(cx),
 7046            [
 7047                Point::new(0, 2)..Point::new(0, 2),
 7048                Point::new(1, 2)..Point::new(1, 2),
 7049                Point::new(2, 2)..Point::new(2, 2)
 7050            ]
 7051        );
 7052
 7053        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7054        assert_eq!(
 7055            editor.text(cx),
 7056            "
 7057                a
 7058                b
 7059                c
 7060            "
 7061            .unindent()
 7062        );
 7063        assert_eq!(
 7064            editor.selections.ranges::<Point>(cx),
 7065            [
 7066                Point::new(0, 1)..Point::new(0, 1),
 7067                Point::new(1, 1)..Point::new(1, 1),
 7068                Point::new(2, 1)..Point::new(2, 1)
 7069            ]
 7070        );
 7071    });
 7072}
 7073
 7074#[gpui::test]
 7075async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7076    init_test(cx, |settings| {
 7077        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7078    });
 7079
 7080    let mut cx = EditorTestContext::new(cx).await;
 7081
 7082    let language = Arc::new(Language::new(
 7083        LanguageConfig {
 7084            brackets: BracketPairConfig {
 7085                pairs: vec![
 7086                    BracketPair {
 7087                        start: "{".to_string(),
 7088                        end: "}".to_string(),
 7089                        close: true,
 7090                        surround: true,
 7091                        newline: true,
 7092                    },
 7093                    BracketPair {
 7094                        start: "(".to_string(),
 7095                        end: ")".to_string(),
 7096                        close: true,
 7097                        surround: true,
 7098                        newline: true,
 7099                    },
 7100                    BracketPair {
 7101                        start: "[".to_string(),
 7102                        end: "]".to_string(),
 7103                        close: false,
 7104                        surround: true,
 7105                        newline: true,
 7106                    },
 7107                ],
 7108                ..Default::default()
 7109            },
 7110            autoclose_before: "})]".to_string(),
 7111            ..Default::default()
 7112        },
 7113        Some(tree_sitter_rust::LANGUAGE.into()),
 7114    ));
 7115
 7116    cx.language_registry().add(language.clone());
 7117    cx.update_buffer(|buffer, cx| {
 7118        buffer.set_language(Some(language), cx);
 7119    });
 7120
 7121    cx.set_state(
 7122        &"
 7123            {(ˇ)}
 7124            [[ˇ]]
 7125            {(ˇ)}
 7126        "
 7127        .unindent(),
 7128    );
 7129
 7130    cx.update_editor(|editor, window, cx| {
 7131        editor.backspace(&Default::default(), window, cx);
 7132        editor.backspace(&Default::default(), window, cx);
 7133    });
 7134
 7135    cx.assert_editor_state(
 7136        &"
 7137            ˇ
 7138            ˇ]]
 7139            ˇ
 7140        "
 7141        .unindent(),
 7142    );
 7143
 7144    cx.update_editor(|editor, window, cx| {
 7145        editor.handle_input("{", window, cx);
 7146        editor.handle_input("{", window, cx);
 7147        editor.move_right(&MoveRight, window, cx);
 7148        editor.move_right(&MoveRight, window, cx);
 7149        editor.move_left(&MoveLeft, window, cx);
 7150        editor.move_left(&MoveLeft, window, cx);
 7151        editor.backspace(&Default::default(), window, cx);
 7152    });
 7153
 7154    cx.assert_editor_state(
 7155        &"
 7156            {ˇ}
 7157            {ˇ}]]
 7158            {ˇ}
 7159        "
 7160        .unindent(),
 7161    );
 7162
 7163    cx.update_editor(|editor, window, cx| {
 7164        editor.backspace(&Default::default(), window, cx);
 7165    });
 7166
 7167    cx.assert_editor_state(
 7168        &"
 7169            ˇ
 7170            ˇ]]
 7171            ˇ
 7172        "
 7173        .unindent(),
 7174    );
 7175}
 7176
 7177#[gpui::test]
 7178async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7179    init_test(cx, |_| {});
 7180
 7181    let language = Arc::new(Language::new(
 7182        LanguageConfig::default(),
 7183        Some(tree_sitter_rust::LANGUAGE.into()),
 7184    ));
 7185
 7186    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7187    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7188    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7189    editor
 7190        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7191        .await;
 7192
 7193    editor.update_in(cx, |editor, window, cx| {
 7194        editor.set_auto_replace_emoji_shortcode(true);
 7195
 7196        editor.handle_input("Hello ", window, cx);
 7197        editor.handle_input(":wave", window, cx);
 7198        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7199
 7200        editor.handle_input(":", window, cx);
 7201        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7202
 7203        editor.handle_input(" :smile", window, cx);
 7204        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7205
 7206        editor.handle_input(":", window, cx);
 7207        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7208
 7209        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7210        editor.handle_input(":wave", window, cx);
 7211        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7212
 7213        editor.handle_input(":", window, cx);
 7214        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7215
 7216        editor.handle_input(":1", window, cx);
 7217        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7218
 7219        editor.handle_input(":", window, cx);
 7220        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7221
 7222        // Ensure shortcode does not get replaced when it is part of a word
 7223        editor.handle_input(" Test:wave", window, cx);
 7224        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7225
 7226        editor.handle_input(":", window, cx);
 7227        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7228
 7229        editor.set_auto_replace_emoji_shortcode(false);
 7230
 7231        // Ensure shortcode does not get replaced when auto replace is off
 7232        editor.handle_input(" :wave", window, cx);
 7233        assert_eq!(
 7234            editor.text(cx),
 7235            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7236        );
 7237
 7238        editor.handle_input(":", window, cx);
 7239        assert_eq!(
 7240            editor.text(cx),
 7241            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7242        );
 7243    });
 7244}
 7245
 7246#[gpui::test]
 7247async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7248    init_test(cx, |_| {});
 7249
 7250    let (text, insertion_ranges) = marked_text_ranges(
 7251        indoc! {"
 7252            ˇ
 7253        "},
 7254        false,
 7255    );
 7256
 7257    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7258    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7259
 7260    _ = editor.update_in(cx, |editor, window, cx| {
 7261        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7262
 7263        editor
 7264            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7265            .unwrap();
 7266
 7267        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7268            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7269            assert_eq!(editor.text(cx), expected_text);
 7270            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7271        }
 7272
 7273        assert(
 7274            editor,
 7275            cx,
 7276            indoc! {"
 7277            type «» =•
 7278            "},
 7279        );
 7280
 7281        assert!(editor.context_menu_visible(), "There should be a matches");
 7282    });
 7283}
 7284
 7285#[gpui::test]
 7286async fn test_snippets(cx: &mut TestAppContext) {
 7287    init_test(cx, |_| {});
 7288
 7289    let (text, insertion_ranges) = marked_text_ranges(
 7290        indoc! {"
 7291            a.ˇ b
 7292            a.ˇ b
 7293            a.ˇ b
 7294        "},
 7295        false,
 7296    );
 7297
 7298    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7299    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7300
 7301    editor.update_in(cx, |editor, window, cx| {
 7302        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7303
 7304        editor
 7305            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7306            .unwrap();
 7307
 7308        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7309            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7310            assert_eq!(editor.text(cx), expected_text);
 7311            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7312        }
 7313
 7314        assert(
 7315            editor,
 7316            cx,
 7317            indoc! {"
 7318                a.f(«one», two, «three») b
 7319                a.f(«one», two, «three») b
 7320                a.f(«one», two, «three») b
 7321            "},
 7322        );
 7323
 7324        // Can't move earlier than the first tab stop
 7325        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7326        assert(
 7327            editor,
 7328            cx,
 7329            indoc! {"
 7330                a.f(«one», two, «three») b
 7331                a.f(«one», two, «three») b
 7332                a.f(«one», two, «three») b
 7333            "},
 7334        );
 7335
 7336        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7337        assert(
 7338            editor,
 7339            cx,
 7340            indoc! {"
 7341                a.f(one, «two», three) b
 7342                a.f(one, «two», three) b
 7343                a.f(one, «two», three) b
 7344            "},
 7345        );
 7346
 7347        editor.move_to_prev_snippet_tabstop(window, cx);
 7348        assert(
 7349            editor,
 7350            cx,
 7351            indoc! {"
 7352                a.f(«one», two, «three») b
 7353                a.f(«one», two, «three») b
 7354                a.f(«one», two, «three») b
 7355            "},
 7356        );
 7357
 7358        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7359        assert(
 7360            editor,
 7361            cx,
 7362            indoc! {"
 7363                a.f(one, «two», three) b
 7364                a.f(one, «two», three) b
 7365                a.f(one, «two», three) b
 7366            "},
 7367        );
 7368        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7369        assert(
 7370            editor,
 7371            cx,
 7372            indoc! {"
 7373                a.f(one, two, three)ˇ b
 7374                a.f(one, two, three)ˇ b
 7375                a.f(one, two, three)ˇ b
 7376            "},
 7377        );
 7378
 7379        // As soon as the last tab stop is reached, snippet state is gone
 7380        editor.move_to_prev_snippet_tabstop(window, cx);
 7381        assert(
 7382            editor,
 7383            cx,
 7384            indoc! {"
 7385                a.f(one, two, three)ˇ b
 7386                a.f(one, two, three)ˇ b
 7387                a.f(one, two, three)ˇ b
 7388            "},
 7389        );
 7390    });
 7391}
 7392
 7393#[gpui::test]
 7394async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7395    init_test(cx, |_| {});
 7396
 7397    let fs = FakeFs::new(cx.executor());
 7398    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7399
 7400    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7401
 7402    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7403    language_registry.add(rust_lang());
 7404    let mut fake_servers = language_registry.register_fake_lsp(
 7405        "Rust",
 7406        FakeLspAdapter {
 7407            capabilities: lsp::ServerCapabilities {
 7408                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7409                ..Default::default()
 7410            },
 7411            ..Default::default()
 7412        },
 7413    );
 7414
 7415    let buffer = project
 7416        .update(cx, |project, cx| {
 7417            project.open_local_buffer(path!("/file.rs"), cx)
 7418        })
 7419        .await
 7420        .unwrap();
 7421
 7422    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7423    let (editor, cx) = cx.add_window_view(|window, cx| {
 7424        build_editor_with_project(project.clone(), buffer, window, cx)
 7425    });
 7426    editor.update_in(cx, |editor, window, cx| {
 7427        editor.set_text("one\ntwo\nthree\n", window, cx)
 7428    });
 7429    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7430
 7431    cx.executor().start_waiting();
 7432    let fake_server = fake_servers.next().await.unwrap();
 7433
 7434    let save = editor
 7435        .update_in(cx, |editor, window, cx| {
 7436            editor.save(true, project.clone(), window, cx)
 7437        })
 7438        .unwrap();
 7439    fake_server
 7440        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7441            assert_eq!(
 7442                params.text_document.uri,
 7443                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7444            );
 7445            assert_eq!(params.options.tab_size, 4);
 7446            Ok(Some(vec![lsp::TextEdit::new(
 7447                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7448                ", ".to_string(),
 7449            )]))
 7450        })
 7451        .next()
 7452        .await;
 7453    cx.executor().start_waiting();
 7454    save.await;
 7455
 7456    assert_eq!(
 7457        editor.update(cx, |editor, cx| editor.text(cx)),
 7458        "one, two\nthree\n"
 7459    );
 7460    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7461
 7462    editor.update_in(cx, |editor, window, cx| {
 7463        editor.set_text("one\ntwo\nthree\n", window, cx)
 7464    });
 7465    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7466
 7467    // Ensure we can still save even if formatting hangs.
 7468    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7469        assert_eq!(
 7470            params.text_document.uri,
 7471            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7472        );
 7473        futures::future::pending::<()>().await;
 7474        unreachable!()
 7475    });
 7476    let save = editor
 7477        .update_in(cx, |editor, window, cx| {
 7478            editor.save(true, project.clone(), window, cx)
 7479        })
 7480        .unwrap();
 7481    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7482    cx.executor().start_waiting();
 7483    save.await;
 7484    assert_eq!(
 7485        editor.update(cx, |editor, cx| editor.text(cx)),
 7486        "one\ntwo\nthree\n"
 7487    );
 7488    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7489
 7490    // For non-dirty buffer, no formatting request should be sent
 7491    let save = editor
 7492        .update_in(cx, |editor, window, cx| {
 7493            editor.save(true, project.clone(), window, cx)
 7494        })
 7495        .unwrap();
 7496    let _pending_format_request = fake_server
 7497        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7498            panic!("Should not be invoked on non-dirty buffer");
 7499        })
 7500        .next();
 7501    cx.executor().start_waiting();
 7502    save.await;
 7503
 7504    // Set rust language override and assert overridden tabsize is sent to language server
 7505    update_test_language_settings(cx, |settings| {
 7506        settings.languages.insert(
 7507            "Rust".into(),
 7508            LanguageSettingsContent {
 7509                tab_size: NonZeroU32::new(8),
 7510                ..Default::default()
 7511            },
 7512        );
 7513    });
 7514
 7515    editor.update_in(cx, |editor, window, cx| {
 7516        editor.set_text("somehting_new\n", window, cx)
 7517    });
 7518    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7519    let save = editor
 7520        .update_in(cx, |editor, window, cx| {
 7521            editor.save(true, project.clone(), window, cx)
 7522        })
 7523        .unwrap();
 7524    fake_server
 7525        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7526            assert_eq!(
 7527                params.text_document.uri,
 7528                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7529            );
 7530            assert_eq!(params.options.tab_size, 8);
 7531            Ok(Some(vec![]))
 7532        })
 7533        .next()
 7534        .await;
 7535    cx.executor().start_waiting();
 7536    save.await;
 7537}
 7538
 7539#[gpui::test]
 7540async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7541    init_test(cx, |_| {});
 7542
 7543    let cols = 4;
 7544    let rows = 10;
 7545    let sample_text_1 = sample_text(rows, cols, 'a');
 7546    assert_eq!(
 7547        sample_text_1,
 7548        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7549    );
 7550    let sample_text_2 = sample_text(rows, cols, 'l');
 7551    assert_eq!(
 7552        sample_text_2,
 7553        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7554    );
 7555    let sample_text_3 = sample_text(rows, cols, 'v');
 7556    assert_eq!(
 7557        sample_text_3,
 7558        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7559    );
 7560
 7561    let fs = FakeFs::new(cx.executor());
 7562    fs.insert_tree(
 7563        path!("/a"),
 7564        json!({
 7565            "main.rs": sample_text_1,
 7566            "other.rs": sample_text_2,
 7567            "lib.rs": sample_text_3,
 7568        }),
 7569    )
 7570    .await;
 7571
 7572    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7573    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7574    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7575
 7576    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7577    language_registry.add(rust_lang());
 7578    let mut fake_servers = language_registry.register_fake_lsp(
 7579        "Rust",
 7580        FakeLspAdapter {
 7581            capabilities: lsp::ServerCapabilities {
 7582                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7583                ..Default::default()
 7584            },
 7585            ..Default::default()
 7586        },
 7587    );
 7588
 7589    let worktree = project.update(cx, |project, cx| {
 7590        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7591        assert_eq!(worktrees.len(), 1);
 7592        worktrees.pop().unwrap()
 7593    });
 7594    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7595
 7596    let buffer_1 = project
 7597        .update(cx, |project, cx| {
 7598            project.open_buffer((worktree_id, "main.rs"), cx)
 7599        })
 7600        .await
 7601        .unwrap();
 7602    let buffer_2 = project
 7603        .update(cx, |project, cx| {
 7604            project.open_buffer((worktree_id, "other.rs"), cx)
 7605        })
 7606        .await
 7607        .unwrap();
 7608    let buffer_3 = project
 7609        .update(cx, |project, cx| {
 7610            project.open_buffer((worktree_id, "lib.rs"), cx)
 7611        })
 7612        .await
 7613        .unwrap();
 7614
 7615    let multi_buffer = cx.new(|cx| {
 7616        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7617        multi_buffer.push_excerpts(
 7618            buffer_1.clone(),
 7619            [
 7620                ExcerptRange {
 7621                    context: Point::new(0, 0)..Point::new(3, 0),
 7622                    primary: None,
 7623                },
 7624                ExcerptRange {
 7625                    context: Point::new(5, 0)..Point::new(7, 0),
 7626                    primary: None,
 7627                },
 7628                ExcerptRange {
 7629                    context: Point::new(9, 0)..Point::new(10, 4),
 7630                    primary: None,
 7631                },
 7632            ],
 7633            cx,
 7634        );
 7635        multi_buffer.push_excerpts(
 7636            buffer_2.clone(),
 7637            [
 7638                ExcerptRange {
 7639                    context: Point::new(0, 0)..Point::new(3, 0),
 7640                    primary: None,
 7641                },
 7642                ExcerptRange {
 7643                    context: Point::new(5, 0)..Point::new(7, 0),
 7644                    primary: None,
 7645                },
 7646                ExcerptRange {
 7647                    context: Point::new(9, 0)..Point::new(10, 4),
 7648                    primary: None,
 7649                },
 7650            ],
 7651            cx,
 7652        );
 7653        multi_buffer.push_excerpts(
 7654            buffer_3.clone(),
 7655            [
 7656                ExcerptRange {
 7657                    context: Point::new(0, 0)..Point::new(3, 0),
 7658                    primary: None,
 7659                },
 7660                ExcerptRange {
 7661                    context: Point::new(5, 0)..Point::new(7, 0),
 7662                    primary: None,
 7663                },
 7664                ExcerptRange {
 7665                    context: Point::new(9, 0)..Point::new(10, 4),
 7666                    primary: None,
 7667                },
 7668            ],
 7669            cx,
 7670        );
 7671        multi_buffer
 7672    });
 7673    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7674        Editor::new(
 7675            EditorMode::Full,
 7676            multi_buffer,
 7677            Some(project.clone()),
 7678            true,
 7679            window,
 7680            cx,
 7681        )
 7682    });
 7683
 7684    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7685        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7686            s.select_ranges(Some(1..2))
 7687        });
 7688        editor.insert("|one|two|three|", window, cx);
 7689    });
 7690    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7691    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7692        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7693            s.select_ranges(Some(60..70))
 7694        });
 7695        editor.insert("|four|five|six|", window, cx);
 7696    });
 7697    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7698
 7699    // First two buffers should be edited, but not the third one.
 7700    assert_eq!(
 7701        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7702        "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}",
 7703    );
 7704    buffer_1.update(cx, |buffer, _| {
 7705        assert!(buffer.is_dirty());
 7706        assert_eq!(
 7707            buffer.text(),
 7708            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7709        )
 7710    });
 7711    buffer_2.update(cx, |buffer, _| {
 7712        assert!(buffer.is_dirty());
 7713        assert_eq!(
 7714            buffer.text(),
 7715            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7716        )
 7717    });
 7718    buffer_3.update(cx, |buffer, _| {
 7719        assert!(!buffer.is_dirty());
 7720        assert_eq!(buffer.text(), sample_text_3,)
 7721    });
 7722    cx.executor().run_until_parked();
 7723
 7724    cx.executor().start_waiting();
 7725    let save = multi_buffer_editor
 7726        .update_in(cx, |editor, window, cx| {
 7727            editor.save(true, project.clone(), window, cx)
 7728        })
 7729        .unwrap();
 7730
 7731    let fake_server = fake_servers.next().await.unwrap();
 7732    fake_server
 7733        .server
 7734        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7735            Ok(Some(vec![lsp::TextEdit::new(
 7736                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7737                format!("[{} formatted]", params.text_document.uri),
 7738            )]))
 7739        })
 7740        .detach();
 7741    save.await;
 7742
 7743    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7744    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7745    assert_eq!(
 7746        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7747        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}"),
 7748    );
 7749    buffer_1.update(cx, |buffer, _| {
 7750        assert!(!buffer.is_dirty());
 7751        assert_eq!(
 7752            buffer.text(),
 7753            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7754        )
 7755    });
 7756    buffer_2.update(cx, |buffer, _| {
 7757        assert!(!buffer.is_dirty());
 7758        assert_eq!(
 7759            buffer.text(),
 7760            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7761        )
 7762    });
 7763    buffer_3.update(cx, |buffer, _| {
 7764        assert!(!buffer.is_dirty());
 7765        assert_eq!(buffer.text(), sample_text_3,)
 7766    });
 7767}
 7768
 7769#[gpui::test]
 7770async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7771    init_test(cx, |_| {});
 7772
 7773    let fs = FakeFs::new(cx.executor());
 7774    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7775
 7776    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7777
 7778    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7779    language_registry.add(rust_lang());
 7780    let mut fake_servers = language_registry.register_fake_lsp(
 7781        "Rust",
 7782        FakeLspAdapter {
 7783            capabilities: lsp::ServerCapabilities {
 7784                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7785                ..Default::default()
 7786            },
 7787            ..Default::default()
 7788        },
 7789    );
 7790
 7791    let buffer = project
 7792        .update(cx, |project, cx| {
 7793            project.open_local_buffer(path!("/file.rs"), cx)
 7794        })
 7795        .await
 7796        .unwrap();
 7797
 7798    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7799    let (editor, cx) = cx.add_window_view(|window, cx| {
 7800        build_editor_with_project(project.clone(), buffer, window, cx)
 7801    });
 7802    editor.update_in(cx, |editor, window, cx| {
 7803        editor.set_text("one\ntwo\nthree\n", window, cx)
 7804    });
 7805    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7806
 7807    cx.executor().start_waiting();
 7808    let fake_server = fake_servers.next().await.unwrap();
 7809
 7810    let save = editor
 7811        .update_in(cx, |editor, window, cx| {
 7812            editor.save(true, project.clone(), window, cx)
 7813        })
 7814        .unwrap();
 7815    fake_server
 7816        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7817            assert_eq!(
 7818                params.text_document.uri,
 7819                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7820            );
 7821            assert_eq!(params.options.tab_size, 4);
 7822            Ok(Some(vec![lsp::TextEdit::new(
 7823                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7824                ", ".to_string(),
 7825            )]))
 7826        })
 7827        .next()
 7828        .await;
 7829    cx.executor().start_waiting();
 7830    save.await;
 7831    assert_eq!(
 7832        editor.update(cx, |editor, cx| editor.text(cx)),
 7833        "one, two\nthree\n"
 7834    );
 7835    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7836
 7837    editor.update_in(cx, |editor, window, cx| {
 7838        editor.set_text("one\ntwo\nthree\n", window, cx)
 7839    });
 7840    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7841
 7842    // Ensure we can still save even if formatting hangs.
 7843    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7844        move |params, _| async move {
 7845            assert_eq!(
 7846                params.text_document.uri,
 7847                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7848            );
 7849            futures::future::pending::<()>().await;
 7850            unreachable!()
 7851        },
 7852    );
 7853    let save = editor
 7854        .update_in(cx, |editor, window, cx| {
 7855            editor.save(true, project.clone(), window, cx)
 7856        })
 7857        .unwrap();
 7858    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7859    cx.executor().start_waiting();
 7860    save.await;
 7861    assert_eq!(
 7862        editor.update(cx, |editor, cx| editor.text(cx)),
 7863        "one\ntwo\nthree\n"
 7864    );
 7865    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7866
 7867    // For non-dirty buffer, no formatting request should be sent
 7868    let save = editor
 7869        .update_in(cx, |editor, window, cx| {
 7870            editor.save(true, project.clone(), window, cx)
 7871        })
 7872        .unwrap();
 7873    let _pending_format_request = fake_server
 7874        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7875            panic!("Should not be invoked on non-dirty buffer");
 7876        })
 7877        .next();
 7878    cx.executor().start_waiting();
 7879    save.await;
 7880
 7881    // Set Rust language override and assert overridden tabsize is sent to language server
 7882    update_test_language_settings(cx, |settings| {
 7883        settings.languages.insert(
 7884            "Rust".into(),
 7885            LanguageSettingsContent {
 7886                tab_size: NonZeroU32::new(8),
 7887                ..Default::default()
 7888            },
 7889        );
 7890    });
 7891
 7892    editor.update_in(cx, |editor, window, cx| {
 7893        editor.set_text("somehting_new\n", window, cx)
 7894    });
 7895    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7896    let save = editor
 7897        .update_in(cx, |editor, window, cx| {
 7898            editor.save(true, project.clone(), window, cx)
 7899        })
 7900        .unwrap();
 7901    fake_server
 7902        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7903            assert_eq!(
 7904                params.text_document.uri,
 7905                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7906            );
 7907            assert_eq!(params.options.tab_size, 8);
 7908            Ok(Some(vec![]))
 7909        })
 7910        .next()
 7911        .await;
 7912    cx.executor().start_waiting();
 7913    save.await;
 7914}
 7915
 7916#[gpui::test]
 7917async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7918    init_test(cx, |settings| {
 7919        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7920            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7921        ))
 7922    });
 7923
 7924    let fs = FakeFs::new(cx.executor());
 7925    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7926
 7927    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7928
 7929    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7930    language_registry.add(Arc::new(Language::new(
 7931        LanguageConfig {
 7932            name: "Rust".into(),
 7933            matcher: LanguageMatcher {
 7934                path_suffixes: vec!["rs".to_string()],
 7935                ..Default::default()
 7936            },
 7937            ..LanguageConfig::default()
 7938        },
 7939        Some(tree_sitter_rust::LANGUAGE.into()),
 7940    )));
 7941    update_test_language_settings(cx, |settings| {
 7942        // Enable Prettier formatting for the same buffer, and ensure
 7943        // LSP is called instead of Prettier.
 7944        settings.defaults.prettier = Some(PrettierSettings {
 7945            allowed: true,
 7946            ..PrettierSettings::default()
 7947        });
 7948    });
 7949    let mut fake_servers = language_registry.register_fake_lsp(
 7950        "Rust",
 7951        FakeLspAdapter {
 7952            capabilities: lsp::ServerCapabilities {
 7953                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7954                ..Default::default()
 7955            },
 7956            ..Default::default()
 7957        },
 7958    );
 7959
 7960    let buffer = project
 7961        .update(cx, |project, cx| {
 7962            project.open_local_buffer(path!("/file.rs"), cx)
 7963        })
 7964        .await
 7965        .unwrap();
 7966
 7967    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7968    let (editor, cx) = cx.add_window_view(|window, cx| {
 7969        build_editor_with_project(project.clone(), buffer, window, cx)
 7970    });
 7971    editor.update_in(cx, |editor, window, cx| {
 7972        editor.set_text("one\ntwo\nthree\n", window, cx)
 7973    });
 7974
 7975    cx.executor().start_waiting();
 7976    let fake_server = fake_servers.next().await.unwrap();
 7977
 7978    let format = editor
 7979        .update_in(cx, |editor, window, cx| {
 7980            editor.perform_format(
 7981                project.clone(),
 7982                FormatTrigger::Manual,
 7983                FormatTarget::Buffers,
 7984                window,
 7985                cx,
 7986            )
 7987        })
 7988        .unwrap();
 7989    fake_server
 7990        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7991            assert_eq!(
 7992                params.text_document.uri,
 7993                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7994            );
 7995            assert_eq!(params.options.tab_size, 4);
 7996            Ok(Some(vec![lsp::TextEdit::new(
 7997                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7998                ", ".to_string(),
 7999            )]))
 8000        })
 8001        .next()
 8002        .await;
 8003    cx.executor().start_waiting();
 8004    format.await;
 8005    assert_eq!(
 8006        editor.update(cx, |editor, cx| editor.text(cx)),
 8007        "one, two\nthree\n"
 8008    );
 8009
 8010    editor.update_in(cx, |editor, window, cx| {
 8011        editor.set_text("one\ntwo\nthree\n", window, cx)
 8012    });
 8013    // Ensure we don't lock if formatting hangs.
 8014    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8015        assert_eq!(
 8016            params.text_document.uri,
 8017            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8018        );
 8019        futures::future::pending::<()>().await;
 8020        unreachable!()
 8021    });
 8022    let format = editor
 8023        .update_in(cx, |editor, window, cx| {
 8024            editor.perform_format(
 8025                project,
 8026                FormatTrigger::Manual,
 8027                FormatTarget::Buffers,
 8028                window,
 8029                cx,
 8030            )
 8031        })
 8032        .unwrap();
 8033    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8034    cx.executor().start_waiting();
 8035    format.await;
 8036    assert_eq!(
 8037        editor.update(cx, |editor, cx| editor.text(cx)),
 8038        "one\ntwo\nthree\n"
 8039    );
 8040}
 8041
 8042#[gpui::test]
 8043async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8044    init_test(cx, |settings| {
 8045        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8046            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8047        ))
 8048    });
 8049
 8050    let fs = FakeFs::new(cx.executor());
 8051    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8052
 8053    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8054
 8055    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8056    language_registry.add(Arc::new(Language::new(
 8057        LanguageConfig {
 8058            name: "TypeScript".into(),
 8059            matcher: LanguageMatcher {
 8060                path_suffixes: vec!["ts".to_string()],
 8061                ..Default::default()
 8062            },
 8063            ..LanguageConfig::default()
 8064        },
 8065        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8066    )));
 8067    update_test_language_settings(cx, |settings| {
 8068        settings.defaults.prettier = Some(PrettierSettings {
 8069            allowed: true,
 8070            ..PrettierSettings::default()
 8071        });
 8072    });
 8073    let mut fake_servers = language_registry.register_fake_lsp(
 8074        "TypeScript",
 8075        FakeLspAdapter {
 8076            capabilities: lsp::ServerCapabilities {
 8077                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8078                ..Default::default()
 8079            },
 8080            ..Default::default()
 8081        },
 8082    );
 8083
 8084    let buffer = project
 8085        .update(cx, |project, cx| {
 8086            project.open_local_buffer(path!("/file.ts"), cx)
 8087        })
 8088        .await
 8089        .unwrap();
 8090
 8091    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8092    let (editor, cx) = cx.add_window_view(|window, cx| {
 8093        build_editor_with_project(project.clone(), buffer, window, cx)
 8094    });
 8095    editor.update_in(cx, |editor, window, cx| {
 8096        editor.set_text(
 8097            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8098            window,
 8099            cx,
 8100        )
 8101    });
 8102
 8103    cx.executor().start_waiting();
 8104    let fake_server = fake_servers.next().await.unwrap();
 8105
 8106    let format = editor
 8107        .update_in(cx, |editor, window, cx| {
 8108            editor.perform_code_action_kind(
 8109                project.clone(),
 8110                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8111                window,
 8112                cx,
 8113            )
 8114        })
 8115        .unwrap();
 8116    fake_server
 8117        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8118            assert_eq!(
 8119                params.text_document.uri,
 8120                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8121            );
 8122            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8123                lsp::CodeAction {
 8124                    title: "Organize Imports".to_string(),
 8125                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8126                    edit: Some(lsp::WorkspaceEdit {
 8127                        changes: Some(
 8128                            [(
 8129                                params.text_document.uri.clone(),
 8130                                vec![lsp::TextEdit::new(
 8131                                    lsp::Range::new(
 8132                                        lsp::Position::new(1, 0),
 8133                                        lsp::Position::new(2, 0),
 8134                                    ),
 8135                                    "".to_string(),
 8136                                )],
 8137                            )]
 8138                            .into_iter()
 8139                            .collect(),
 8140                        ),
 8141                        ..Default::default()
 8142                    }),
 8143                    ..Default::default()
 8144                },
 8145            )]))
 8146        })
 8147        .next()
 8148        .await;
 8149    cx.executor().start_waiting();
 8150    format.await;
 8151    assert_eq!(
 8152        editor.update(cx, |editor, cx| editor.text(cx)),
 8153        "import { a } from 'module';\n\nconst x = a;\n"
 8154    );
 8155
 8156    editor.update_in(cx, |editor, window, cx| {
 8157        editor.set_text(
 8158            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8159            window,
 8160            cx,
 8161        )
 8162    });
 8163    // Ensure we don't lock if code action hangs.
 8164    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8165        move |params, _| async move {
 8166            assert_eq!(
 8167                params.text_document.uri,
 8168                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8169            );
 8170            futures::future::pending::<()>().await;
 8171            unreachable!()
 8172        },
 8173    );
 8174    let format = editor
 8175        .update_in(cx, |editor, window, cx| {
 8176            editor.perform_code_action_kind(
 8177                project,
 8178                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8179                window,
 8180                cx,
 8181            )
 8182        })
 8183        .unwrap();
 8184    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8185    cx.executor().start_waiting();
 8186    format.await;
 8187    assert_eq!(
 8188        editor.update(cx, |editor, cx| editor.text(cx)),
 8189        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8190    );
 8191}
 8192
 8193#[gpui::test]
 8194async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8195    init_test(cx, |_| {});
 8196
 8197    let mut cx = EditorLspTestContext::new_rust(
 8198        lsp::ServerCapabilities {
 8199            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8200            ..Default::default()
 8201        },
 8202        cx,
 8203    )
 8204    .await;
 8205
 8206    cx.set_state(indoc! {"
 8207        one.twoˇ
 8208    "});
 8209
 8210    // The format request takes a long time. When it completes, it inserts
 8211    // a newline and an indent before the `.`
 8212    cx.lsp
 8213        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8214            let executor = cx.background_executor().clone();
 8215            async move {
 8216                executor.timer(Duration::from_millis(100)).await;
 8217                Ok(Some(vec![lsp::TextEdit {
 8218                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8219                    new_text: "\n    ".into(),
 8220                }]))
 8221            }
 8222        });
 8223
 8224    // Submit a format request.
 8225    let format_1 = cx
 8226        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8227        .unwrap();
 8228    cx.executor().run_until_parked();
 8229
 8230    // Submit a second format request.
 8231    let format_2 = cx
 8232        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8233        .unwrap();
 8234    cx.executor().run_until_parked();
 8235
 8236    // Wait for both format requests to complete
 8237    cx.executor().advance_clock(Duration::from_millis(200));
 8238    cx.executor().start_waiting();
 8239    format_1.await.unwrap();
 8240    cx.executor().start_waiting();
 8241    format_2.await.unwrap();
 8242
 8243    // The formatting edits only happens once.
 8244    cx.assert_editor_state(indoc! {"
 8245        one
 8246            .twoˇ
 8247    "});
 8248}
 8249
 8250#[gpui::test]
 8251async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8252    init_test(cx, |settings| {
 8253        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8254    });
 8255
 8256    let mut cx = EditorLspTestContext::new_rust(
 8257        lsp::ServerCapabilities {
 8258            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8259            ..Default::default()
 8260        },
 8261        cx,
 8262    )
 8263    .await;
 8264
 8265    // Set up a buffer white some trailing whitespace and no trailing newline.
 8266    cx.set_state(
 8267        &[
 8268            "one ",   //
 8269            "twoˇ",   //
 8270            "three ", //
 8271            "four",   //
 8272        ]
 8273        .join("\n"),
 8274    );
 8275
 8276    // Submit a format request.
 8277    let format = cx
 8278        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8279        .unwrap();
 8280
 8281    // Record which buffer changes have been sent to the language server
 8282    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8283    cx.lsp
 8284        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8285            let buffer_changes = buffer_changes.clone();
 8286            move |params, _| {
 8287                buffer_changes.lock().extend(
 8288                    params
 8289                        .content_changes
 8290                        .into_iter()
 8291                        .map(|e| (e.range.unwrap(), e.text)),
 8292                );
 8293            }
 8294        });
 8295
 8296    // Handle formatting requests to the language server.
 8297    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8298        let buffer_changes = buffer_changes.clone();
 8299        move |_, _| {
 8300            // When formatting is requested, trailing whitespace has already been stripped,
 8301            // and the trailing newline has already been added.
 8302            assert_eq!(
 8303                &buffer_changes.lock()[1..],
 8304                &[
 8305                    (
 8306                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8307                        "".into()
 8308                    ),
 8309                    (
 8310                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8311                        "".into()
 8312                    ),
 8313                    (
 8314                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8315                        "\n".into()
 8316                    ),
 8317                ]
 8318            );
 8319
 8320            // Insert blank lines between each line of the buffer.
 8321            async move {
 8322                Ok(Some(vec![
 8323                    lsp::TextEdit {
 8324                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8325                        new_text: "\n".into(),
 8326                    },
 8327                    lsp::TextEdit {
 8328                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8329                        new_text: "\n".into(),
 8330                    },
 8331                ]))
 8332            }
 8333        }
 8334    });
 8335
 8336    // After formatting the buffer, the trailing whitespace is stripped,
 8337    // a newline is appended, and the edits provided by the language server
 8338    // have been applied.
 8339    format.await.unwrap();
 8340    cx.assert_editor_state(
 8341        &[
 8342            "one",   //
 8343            "",      //
 8344            "twoˇ",  //
 8345            "",      //
 8346            "three", //
 8347            "four",  //
 8348            "",      //
 8349        ]
 8350        .join("\n"),
 8351    );
 8352
 8353    // Undoing the formatting undoes the trailing whitespace removal, the
 8354    // trailing newline, and the LSP edits.
 8355    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8356    cx.assert_editor_state(
 8357        &[
 8358            "one ",   //
 8359            "twoˇ",   //
 8360            "three ", //
 8361            "four",   //
 8362        ]
 8363        .join("\n"),
 8364    );
 8365}
 8366
 8367#[gpui::test]
 8368async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8369    cx: &mut TestAppContext,
 8370) {
 8371    init_test(cx, |_| {});
 8372
 8373    cx.update(|cx| {
 8374        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8375            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8376                settings.auto_signature_help = Some(true);
 8377            });
 8378        });
 8379    });
 8380
 8381    let mut cx = EditorLspTestContext::new_rust(
 8382        lsp::ServerCapabilities {
 8383            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8384                ..Default::default()
 8385            }),
 8386            ..Default::default()
 8387        },
 8388        cx,
 8389    )
 8390    .await;
 8391
 8392    let language = Language::new(
 8393        LanguageConfig {
 8394            name: "Rust".into(),
 8395            brackets: BracketPairConfig {
 8396                pairs: vec![
 8397                    BracketPair {
 8398                        start: "{".to_string(),
 8399                        end: "}".to_string(),
 8400                        close: true,
 8401                        surround: true,
 8402                        newline: true,
 8403                    },
 8404                    BracketPair {
 8405                        start: "(".to_string(),
 8406                        end: ")".to_string(),
 8407                        close: true,
 8408                        surround: true,
 8409                        newline: true,
 8410                    },
 8411                    BracketPair {
 8412                        start: "/*".to_string(),
 8413                        end: " */".to_string(),
 8414                        close: true,
 8415                        surround: true,
 8416                        newline: true,
 8417                    },
 8418                    BracketPair {
 8419                        start: "[".to_string(),
 8420                        end: "]".to_string(),
 8421                        close: false,
 8422                        surround: false,
 8423                        newline: true,
 8424                    },
 8425                    BracketPair {
 8426                        start: "\"".to_string(),
 8427                        end: "\"".to_string(),
 8428                        close: true,
 8429                        surround: true,
 8430                        newline: false,
 8431                    },
 8432                    BracketPair {
 8433                        start: "<".to_string(),
 8434                        end: ">".to_string(),
 8435                        close: false,
 8436                        surround: true,
 8437                        newline: true,
 8438                    },
 8439                ],
 8440                ..Default::default()
 8441            },
 8442            autoclose_before: "})]".to_string(),
 8443            ..Default::default()
 8444        },
 8445        Some(tree_sitter_rust::LANGUAGE.into()),
 8446    );
 8447    let language = Arc::new(language);
 8448
 8449    cx.language_registry().add(language.clone());
 8450    cx.update_buffer(|buffer, cx| {
 8451        buffer.set_language(Some(language), cx);
 8452    });
 8453
 8454    cx.set_state(
 8455        &r#"
 8456            fn main() {
 8457                sampleˇ
 8458            }
 8459        "#
 8460        .unindent(),
 8461    );
 8462
 8463    cx.update_editor(|editor, window, cx| {
 8464        editor.handle_input("(", window, cx);
 8465    });
 8466    cx.assert_editor_state(
 8467        &"
 8468            fn main() {
 8469                sample(ˇ)
 8470            }
 8471        "
 8472        .unindent(),
 8473    );
 8474
 8475    let mocked_response = lsp::SignatureHelp {
 8476        signatures: vec![lsp::SignatureInformation {
 8477            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8478            documentation: None,
 8479            parameters: Some(vec![
 8480                lsp::ParameterInformation {
 8481                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8482                    documentation: None,
 8483                },
 8484                lsp::ParameterInformation {
 8485                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8486                    documentation: None,
 8487                },
 8488            ]),
 8489            active_parameter: None,
 8490        }],
 8491        active_signature: Some(0),
 8492        active_parameter: Some(0),
 8493    };
 8494    handle_signature_help_request(&mut cx, mocked_response).await;
 8495
 8496    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8497        .await;
 8498
 8499    cx.editor(|editor, _, _| {
 8500        let signature_help_state = editor.signature_help_state.popover().cloned();
 8501        assert_eq!(
 8502            signature_help_state.unwrap().label,
 8503            "param1: u8, param2: u8"
 8504        );
 8505    });
 8506}
 8507
 8508#[gpui::test]
 8509async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8510    init_test(cx, |_| {});
 8511
 8512    cx.update(|cx| {
 8513        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8514            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8515                settings.auto_signature_help = Some(false);
 8516                settings.show_signature_help_after_edits = Some(false);
 8517            });
 8518        });
 8519    });
 8520
 8521    let mut cx = EditorLspTestContext::new_rust(
 8522        lsp::ServerCapabilities {
 8523            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8524                ..Default::default()
 8525            }),
 8526            ..Default::default()
 8527        },
 8528        cx,
 8529    )
 8530    .await;
 8531
 8532    let language = Language::new(
 8533        LanguageConfig {
 8534            name: "Rust".into(),
 8535            brackets: BracketPairConfig {
 8536                pairs: vec![
 8537                    BracketPair {
 8538                        start: "{".to_string(),
 8539                        end: "}".to_string(),
 8540                        close: true,
 8541                        surround: true,
 8542                        newline: true,
 8543                    },
 8544                    BracketPair {
 8545                        start: "(".to_string(),
 8546                        end: ")".to_string(),
 8547                        close: true,
 8548                        surround: true,
 8549                        newline: true,
 8550                    },
 8551                    BracketPair {
 8552                        start: "/*".to_string(),
 8553                        end: " */".to_string(),
 8554                        close: true,
 8555                        surround: true,
 8556                        newline: true,
 8557                    },
 8558                    BracketPair {
 8559                        start: "[".to_string(),
 8560                        end: "]".to_string(),
 8561                        close: false,
 8562                        surround: false,
 8563                        newline: true,
 8564                    },
 8565                    BracketPair {
 8566                        start: "\"".to_string(),
 8567                        end: "\"".to_string(),
 8568                        close: true,
 8569                        surround: true,
 8570                        newline: false,
 8571                    },
 8572                    BracketPair {
 8573                        start: "<".to_string(),
 8574                        end: ">".to_string(),
 8575                        close: false,
 8576                        surround: true,
 8577                        newline: true,
 8578                    },
 8579                ],
 8580                ..Default::default()
 8581            },
 8582            autoclose_before: "})]".to_string(),
 8583            ..Default::default()
 8584        },
 8585        Some(tree_sitter_rust::LANGUAGE.into()),
 8586    );
 8587    let language = Arc::new(language);
 8588
 8589    cx.language_registry().add(language.clone());
 8590    cx.update_buffer(|buffer, cx| {
 8591        buffer.set_language(Some(language), cx);
 8592    });
 8593
 8594    // Ensure that signature_help is not called when no signature help is enabled.
 8595    cx.set_state(
 8596        &r#"
 8597            fn main() {
 8598                sampleˇ
 8599            }
 8600        "#
 8601        .unindent(),
 8602    );
 8603    cx.update_editor(|editor, window, cx| {
 8604        editor.handle_input("(", window, cx);
 8605    });
 8606    cx.assert_editor_state(
 8607        &"
 8608            fn main() {
 8609                sample(ˇ)
 8610            }
 8611        "
 8612        .unindent(),
 8613    );
 8614    cx.editor(|editor, _, _| {
 8615        assert!(editor.signature_help_state.task().is_none());
 8616    });
 8617
 8618    let mocked_response = lsp::SignatureHelp {
 8619        signatures: vec![lsp::SignatureInformation {
 8620            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8621            documentation: None,
 8622            parameters: Some(vec![
 8623                lsp::ParameterInformation {
 8624                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8625                    documentation: None,
 8626                },
 8627                lsp::ParameterInformation {
 8628                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8629                    documentation: None,
 8630                },
 8631            ]),
 8632            active_parameter: None,
 8633        }],
 8634        active_signature: Some(0),
 8635        active_parameter: Some(0),
 8636    };
 8637
 8638    // Ensure that signature_help is called when enabled afte edits
 8639    cx.update(|_, cx| {
 8640        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8641            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8642                settings.auto_signature_help = Some(false);
 8643                settings.show_signature_help_after_edits = Some(true);
 8644            });
 8645        });
 8646    });
 8647    cx.set_state(
 8648        &r#"
 8649            fn main() {
 8650                sampleˇ
 8651            }
 8652        "#
 8653        .unindent(),
 8654    );
 8655    cx.update_editor(|editor, window, cx| {
 8656        editor.handle_input("(", window, cx);
 8657    });
 8658    cx.assert_editor_state(
 8659        &"
 8660            fn main() {
 8661                sample(ˇ)
 8662            }
 8663        "
 8664        .unindent(),
 8665    );
 8666    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8667    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8668        .await;
 8669    cx.update_editor(|editor, _, _| {
 8670        let signature_help_state = editor.signature_help_state.popover().cloned();
 8671        assert!(signature_help_state.is_some());
 8672        assert_eq!(
 8673            signature_help_state.unwrap().label,
 8674            "param1: u8, param2: u8"
 8675        );
 8676        editor.signature_help_state = SignatureHelpState::default();
 8677    });
 8678
 8679    // Ensure that signature_help is called when auto signature help override is enabled
 8680    cx.update(|_, cx| {
 8681        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8682            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8683                settings.auto_signature_help = Some(true);
 8684                settings.show_signature_help_after_edits = Some(false);
 8685            });
 8686        });
 8687    });
 8688    cx.set_state(
 8689        &r#"
 8690            fn main() {
 8691                sampleˇ
 8692            }
 8693        "#
 8694        .unindent(),
 8695    );
 8696    cx.update_editor(|editor, window, cx| {
 8697        editor.handle_input("(", window, cx);
 8698    });
 8699    cx.assert_editor_state(
 8700        &"
 8701            fn main() {
 8702                sample(ˇ)
 8703            }
 8704        "
 8705        .unindent(),
 8706    );
 8707    handle_signature_help_request(&mut cx, mocked_response).await;
 8708    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8709        .await;
 8710    cx.editor(|editor, _, _| {
 8711        let signature_help_state = editor.signature_help_state.popover().cloned();
 8712        assert!(signature_help_state.is_some());
 8713        assert_eq!(
 8714            signature_help_state.unwrap().label,
 8715            "param1: u8, param2: u8"
 8716        );
 8717    });
 8718}
 8719
 8720#[gpui::test]
 8721async fn test_signature_help(cx: &mut TestAppContext) {
 8722    init_test(cx, |_| {});
 8723    cx.update(|cx| {
 8724        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8725            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8726                settings.auto_signature_help = Some(true);
 8727            });
 8728        });
 8729    });
 8730
 8731    let mut cx = EditorLspTestContext::new_rust(
 8732        lsp::ServerCapabilities {
 8733            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8734                ..Default::default()
 8735            }),
 8736            ..Default::default()
 8737        },
 8738        cx,
 8739    )
 8740    .await;
 8741
 8742    // A test that directly calls `show_signature_help`
 8743    cx.update_editor(|editor, window, cx| {
 8744        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8745    });
 8746
 8747    let mocked_response = lsp::SignatureHelp {
 8748        signatures: vec![lsp::SignatureInformation {
 8749            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8750            documentation: None,
 8751            parameters: Some(vec![
 8752                lsp::ParameterInformation {
 8753                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8754                    documentation: None,
 8755                },
 8756                lsp::ParameterInformation {
 8757                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8758                    documentation: None,
 8759                },
 8760            ]),
 8761            active_parameter: None,
 8762        }],
 8763        active_signature: Some(0),
 8764        active_parameter: Some(0),
 8765    };
 8766    handle_signature_help_request(&mut cx, mocked_response).await;
 8767
 8768    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8769        .await;
 8770
 8771    cx.editor(|editor, _, _| {
 8772        let signature_help_state = editor.signature_help_state.popover().cloned();
 8773        assert!(signature_help_state.is_some());
 8774        assert_eq!(
 8775            signature_help_state.unwrap().label,
 8776            "param1: u8, param2: u8"
 8777        );
 8778    });
 8779
 8780    // When exiting outside from inside the brackets, `signature_help` is closed.
 8781    cx.set_state(indoc! {"
 8782        fn main() {
 8783            sample(ˇ);
 8784        }
 8785
 8786        fn sample(param1: u8, param2: u8) {}
 8787    "});
 8788
 8789    cx.update_editor(|editor, window, cx| {
 8790        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8791    });
 8792
 8793    let mocked_response = lsp::SignatureHelp {
 8794        signatures: Vec::new(),
 8795        active_signature: None,
 8796        active_parameter: None,
 8797    };
 8798    handle_signature_help_request(&mut cx, mocked_response).await;
 8799
 8800    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8801        .await;
 8802
 8803    cx.editor(|editor, _, _| {
 8804        assert!(!editor.signature_help_state.is_shown());
 8805    });
 8806
 8807    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8808    cx.set_state(indoc! {"
 8809        fn main() {
 8810            sample(ˇ);
 8811        }
 8812
 8813        fn sample(param1: u8, param2: u8) {}
 8814    "});
 8815
 8816    let mocked_response = lsp::SignatureHelp {
 8817        signatures: vec![lsp::SignatureInformation {
 8818            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8819            documentation: None,
 8820            parameters: Some(vec![
 8821                lsp::ParameterInformation {
 8822                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8823                    documentation: None,
 8824                },
 8825                lsp::ParameterInformation {
 8826                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8827                    documentation: None,
 8828                },
 8829            ]),
 8830            active_parameter: None,
 8831        }],
 8832        active_signature: Some(0),
 8833        active_parameter: Some(0),
 8834    };
 8835    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8836    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8837        .await;
 8838    cx.editor(|editor, _, _| {
 8839        assert!(editor.signature_help_state.is_shown());
 8840    });
 8841
 8842    // Restore the popover with more parameter input
 8843    cx.set_state(indoc! {"
 8844        fn main() {
 8845            sample(param1, param2ˇ);
 8846        }
 8847
 8848        fn sample(param1: u8, param2: u8) {}
 8849    "});
 8850
 8851    let mocked_response = lsp::SignatureHelp {
 8852        signatures: vec![lsp::SignatureInformation {
 8853            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8854            documentation: None,
 8855            parameters: Some(vec![
 8856                lsp::ParameterInformation {
 8857                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8858                    documentation: None,
 8859                },
 8860                lsp::ParameterInformation {
 8861                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8862                    documentation: None,
 8863                },
 8864            ]),
 8865            active_parameter: None,
 8866        }],
 8867        active_signature: Some(0),
 8868        active_parameter: Some(1),
 8869    };
 8870    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8871    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8872        .await;
 8873
 8874    // When selecting a range, the popover is gone.
 8875    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8876    cx.update_editor(|editor, window, cx| {
 8877        editor.change_selections(None, window, cx, |s| {
 8878            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8879        })
 8880    });
 8881    cx.assert_editor_state(indoc! {"
 8882        fn main() {
 8883            sample(param1, «ˇparam2»);
 8884        }
 8885
 8886        fn sample(param1: u8, param2: u8) {}
 8887    "});
 8888    cx.editor(|editor, _, _| {
 8889        assert!(!editor.signature_help_state.is_shown());
 8890    });
 8891
 8892    // When unselecting again, the popover is back if within the brackets.
 8893    cx.update_editor(|editor, window, cx| {
 8894        editor.change_selections(None, window, cx, |s| {
 8895            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8896        })
 8897    });
 8898    cx.assert_editor_state(indoc! {"
 8899        fn main() {
 8900            sample(param1, ˇparam2);
 8901        }
 8902
 8903        fn sample(param1: u8, param2: u8) {}
 8904    "});
 8905    handle_signature_help_request(&mut cx, mocked_response).await;
 8906    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8907        .await;
 8908    cx.editor(|editor, _, _| {
 8909        assert!(editor.signature_help_state.is_shown());
 8910    });
 8911
 8912    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8913    cx.update_editor(|editor, window, cx| {
 8914        editor.change_selections(None, window, cx, |s| {
 8915            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8916            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8917        })
 8918    });
 8919    cx.assert_editor_state(indoc! {"
 8920        fn main() {
 8921            sample(param1, ˇparam2);
 8922        }
 8923
 8924        fn sample(param1: u8, param2: u8) {}
 8925    "});
 8926
 8927    let mocked_response = lsp::SignatureHelp {
 8928        signatures: vec![lsp::SignatureInformation {
 8929            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8930            documentation: None,
 8931            parameters: Some(vec![
 8932                lsp::ParameterInformation {
 8933                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8934                    documentation: None,
 8935                },
 8936                lsp::ParameterInformation {
 8937                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8938                    documentation: None,
 8939                },
 8940            ]),
 8941            active_parameter: None,
 8942        }],
 8943        active_signature: Some(0),
 8944        active_parameter: Some(1),
 8945    };
 8946    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8947    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8948        .await;
 8949    cx.update_editor(|editor, _, cx| {
 8950        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8951    });
 8952    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8953        .await;
 8954    cx.update_editor(|editor, window, cx| {
 8955        editor.change_selections(None, window, cx, |s| {
 8956            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8957        })
 8958    });
 8959    cx.assert_editor_state(indoc! {"
 8960        fn main() {
 8961            sample(param1, «ˇparam2»);
 8962        }
 8963
 8964        fn sample(param1: u8, param2: u8) {}
 8965    "});
 8966    cx.update_editor(|editor, window, cx| {
 8967        editor.change_selections(None, window, cx, |s| {
 8968            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8969        })
 8970    });
 8971    cx.assert_editor_state(indoc! {"
 8972        fn main() {
 8973            sample(param1, ˇparam2);
 8974        }
 8975
 8976        fn sample(param1: u8, param2: u8) {}
 8977    "});
 8978    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8979        .await;
 8980}
 8981
 8982#[gpui::test]
 8983async fn test_completion(cx: &mut TestAppContext) {
 8984    init_test(cx, |_| {});
 8985
 8986    let mut cx = EditorLspTestContext::new_rust(
 8987        lsp::ServerCapabilities {
 8988            completion_provider: Some(lsp::CompletionOptions {
 8989                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8990                resolve_provider: Some(true),
 8991                ..Default::default()
 8992            }),
 8993            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8994            ..Default::default()
 8995        },
 8996        cx,
 8997    )
 8998    .await;
 8999    let counter = Arc::new(AtomicUsize::new(0));
 9000
 9001    cx.set_state(indoc! {"
 9002        oneˇ
 9003        two
 9004        three
 9005    "});
 9006    cx.simulate_keystroke(".");
 9007    handle_completion_request(
 9008        &mut cx,
 9009        indoc! {"
 9010            one.|<>
 9011            two
 9012            three
 9013        "},
 9014        vec!["first_completion", "second_completion"],
 9015        counter.clone(),
 9016    )
 9017    .await;
 9018    cx.condition(|editor, _| editor.context_menu_visible())
 9019        .await;
 9020    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9021
 9022    let _handler = handle_signature_help_request(
 9023        &mut cx,
 9024        lsp::SignatureHelp {
 9025            signatures: vec![lsp::SignatureInformation {
 9026                label: "test signature".to_string(),
 9027                documentation: None,
 9028                parameters: Some(vec![lsp::ParameterInformation {
 9029                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9030                    documentation: None,
 9031                }]),
 9032                active_parameter: None,
 9033            }],
 9034            active_signature: None,
 9035            active_parameter: None,
 9036        },
 9037    );
 9038    cx.update_editor(|editor, window, cx| {
 9039        assert!(
 9040            !editor.signature_help_state.is_shown(),
 9041            "No signature help was called for"
 9042        );
 9043        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9044    });
 9045    cx.run_until_parked();
 9046    cx.update_editor(|editor, _, _| {
 9047        assert!(
 9048            !editor.signature_help_state.is_shown(),
 9049            "No signature help should be shown when completions menu is open"
 9050        );
 9051    });
 9052
 9053    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9054        editor.context_menu_next(&Default::default(), window, cx);
 9055        editor
 9056            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9057            .unwrap()
 9058    });
 9059    cx.assert_editor_state(indoc! {"
 9060        one.second_completionˇ
 9061        two
 9062        three
 9063    "});
 9064
 9065    handle_resolve_completion_request(
 9066        &mut cx,
 9067        Some(vec![
 9068            (
 9069                //This overlaps with the primary completion edit which is
 9070                //misbehavior from the LSP spec, test that we filter it out
 9071                indoc! {"
 9072                    one.second_ˇcompletion
 9073                    two
 9074                    threeˇ
 9075                "},
 9076                "overlapping additional edit",
 9077            ),
 9078            (
 9079                indoc! {"
 9080                    one.second_completion
 9081                    two
 9082                    threeˇ
 9083                "},
 9084                "\nadditional edit",
 9085            ),
 9086        ]),
 9087    )
 9088    .await;
 9089    apply_additional_edits.await.unwrap();
 9090    cx.assert_editor_state(indoc! {"
 9091        one.second_completionˇ
 9092        two
 9093        three
 9094        additional edit
 9095    "});
 9096
 9097    cx.set_state(indoc! {"
 9098        one.second_completion
 9099        twoˇ
 9100        threeˇ
 9101        additional edit
 9102    "});
 9103    cx.simulate_keystroke(" ");
 9104    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9105    cx.simulate_keystroke("s");
 9106    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9107
 9108    cx.assert_editor_state(indoc! {"
 9109        one.second_completion
 9110        two sˇ
 9111        three sˇ
 9112        additional edit
 9113    "});
 9114    handle_completion_request(
 9115        &mut cx,
 9116        indoc! {"
 9117            one.second_completion
 9118            two s
 9119            three <s|>
 9120            additional edit
 9121        "},
 9122        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9123        counter.clone(),
 9124    )
 9125    .await;
 9126    cx.condition(|editor, _| editor.context_menu_visible())
 9127        .await;
 9128    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9129
 9130    cx.simulate_keystroke("i");
 9131
 9132    handle_completion_request(
 9133        &mut cx,
 9134        indoc! {"
 9135            one.second_completion
 9136            two si
 9137            three <si|>
 9138            additional edit
 9139        "},
 9140        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9141        counter.clone(),
 9142    )
 9143    .await;
 9144    cx.condition(|editor, _| editor.context_menu_visible())
 9145        .await;
 9146    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9147
 9148    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9149        editor
 9150            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9151            .unwrap()
 9152    });
 9153    cx.assert_editor_state(indoc! {"
 9154        one.second_completion
 9155        two sixth_completionˇ
 9156        three sixth_completionˇ
 9157        additional edit
 9158    "});
 9159
 9160    apply_additional_edits.await.unwrap();
 9161
 9162    update_test_language_settings(&mut cx, |settings| {
 9163        settings.defaults.show_completions_on_input = Some(false);
 9164    });
 9165    cx.set_state("editorˇ");
 9166    cx.simulate_keystroke(".");
 9167    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9168    cx.simulate_keystroke("c");
 9169    cx.simulate_keystroke("l");
 9170    cx.simulate_keystroke("o");
 9171    cx.assert_editor_state("editor.cloˇ");
 9172    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9173    cx.update_editor(|editor, window, cx| {
 9174        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9175    });
 9176    handle_completion_request(
 9177        &mut cx,
 9178        "editor.<clo|>",
 9179        vec!["close", "clobber"],
 9180        counter.clone(),
 9181    )
 9182    .await;
 9183    cx.condition(|editor, _| editor.context_menu_visible())
 9184        .await;
 9185    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9186
 9187    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9188        editor
 9189            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9190            .unwrap()
 9191    });
 9192    cx.assert_editor_state("editor.closeˇ");
 9193    handle_resolve_completion_request(&mut cx, None).await;
 9194    apply_additional_edits.await.unwrap();
 9195}
 9196
 9197#[gpui::test]
 9198async fn test_multiline_completion(cx: &mut TestAppContext) {
 9199    init_test(cx, |_| {});
 9200
 9201    let fs = FakeFs::new(cx.executor());
 9202    fs.insert_tree(
 9203        path!("/a"),
 9204        json!({
 9205            "main.ts": "a",
 9206        }),
 9207    )
 9208    .await;
 9209
 9210    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9211    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9212    let typescript_language = Arc::new(Language::new(
 9213        LanguageConfig {
 9214            name: "TypeScript".into(),
 9215            matcher: LanguageMatcher {
 9216                path_suffixes: vec!["ts".to_string()],
 9217                ..LanguageMatcher::default()
 9218            },
 9219            line_comments: vec!["// ".into()],
 9220            ..LanguageConfig::default()
 9221        },
 9222        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9223    ));
 9224    language_registry.add(typescript_language.clone());
 9225    let mut fake_servers = language_registry.register_fake_lsp(
 9226        "TypeScript",
 9227        FakeLspAdapter {
 9228            capabilities: lsp::ServerCapabilities {
 9229                completion_provider: Some(lsp::CompletionOptions {
 9230                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9231                    ..lsp::CompletionOptions::default()
 9232                }),
 9233                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9234                ..lsp::ServerCapabilities::default()
 9235            },
 9236            // Emulate vtsls label generation
 9237            label_for_completion: Some(Box::new(|item, _| {
 9238                let text = if let Some(description) = item
 9239                    .label_details
 9240                    .as_ref()
 9241                    .and_then(|label_details| label_details.description.as_ref())
 9242                {
 9243                    format!("{} {}", item.label, description)
 9244                } else if let Some(detail) = &item.detail {
 9245                    format!("{} {}", item.label, detail)
 9246                } else {
 9247                    item.label.clone()
 9248                };
 9249                let len = text.len();
 9250                Some(language::CodeLabel {
 9251                    text,
 9252                    runs: Vec::new(),
 9253                    filter_range: 0..len,
 9254                })
 9255            })),
 9256            ..FakeLspAdapter::default()
 9257        },
 9258    );
 9259    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9260    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9261    let worktree_id = workspace
 9262        .update(cx, |workspace, _window, cx| {
 9263            workspace.project().update(cx, |project, cx| {
 9264                project.worktrees(cx).next().unwrap().read(cx).id()
 9265            })
 9266        })
 9267        .unwrap();
 9268    let _buffer = project
 9269        .update(cx, |project, cx| {
 9270            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9271        })
 9272        .await
 9273        .unwrap();
 9274    let editor = workspace
 9275        .update(cx, |workspace, window, cx| {
 9276            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9277        })
 9278        .unwrap()
 9279        .await
 9280        .unwrap()
 9281        .downcast::<Editor>()
 9282        .unwrap();
 9283    let fake_server = fake_servers.next().await.unwrap();
 9284
 9285    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9286    let multiline_label_2 = "a\nb\nc\n";
 9287    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9288    let multiline_description = "d\ne\nf\n";
 9289    let multiline_detail_2 = "g\nh\ni\n";
 9290
 9291    let mut completion_handle =
 9292        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9293            Ok(Some(lsp::CompletionResponse::Array(vec![
 9294                lsp::CompletionItem {
 9295                    label: multiline_label.to_string(),
 9296                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9297                        range: lsp::Range {
 9298                            start: lsp::Position {
 9299                                line: params.text_document_position.position.line,
 9300                                character: params.text_document_position.position.character,
 9301                            },
 9302                            end: lsp::Position {
 9303                                line: params.text_document_position.position.line,
 9304                                character: params.text_document_position.position.character,
 9305                            },
 9306                        },
 9307                        new_text: "new_text_1".to_string(),
 9308                    })),
 9309                    ..lsp::CompletionItem::default()
 9310                },
 9311                lsp::CompletionItem {
 9312                    label: "single line label 1".to_string(),
 9313                    detail: Some(multiline_detail.to_string()),
 9314                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9315                        range: lsp::Range {
 9316                            start: lsp::Position {
 9317                                line: params.text_document_position.position.line,
 9318                                character: params.text_document_position.position.character,
 9319                            },
 9320                            end: lsp::Position {
 9321                                line: params.text_document_position.position.line,
 9322                                character: params.text_document_position.position.character,
 9323                            },
 9324                        },
 9325                        new_text: "new_text_2".to_string(),
 9326                    })),
 9327                    ..lsp::CompletionItem::default()
 9328                },
 9329                lsp::CompletionItem {
 9330                    label: "single line label 2".to_string(),
 9331                    label_details: Some(lsp::CompletionItemLabelDetails {
 9332                        description: Some(multiline_description.to_string()),
 9333                        detail: None,
 9334                    }),
 9335                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9336                        range: lsp::Range {
 9337                            start: lsp::Position {
 9338                                line: params.text_document_position.position.line,
 9339                                character: params.text_document_position.position.character,
 9340                            },
 9341                            end: lsp::Position {
 9342                                line: params.text_document_position.position.line,
 9343                                character: params.text_document_position.position.character,
 9344                            },
 9345                        },
 9346                        new_text: "new_text_2".to_string(),
 9347                    })),
 9348                    ..lsp::CompletionItem::default()
 9349                },
 9350                lsp::CompletionItem {
 9351                    label: multiline_label_2.to_string(),
 9352                    detail: Some(multiline_detail_2.to_string()),
 9353                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9354                        range: lsp::Range {
 9355                            start: lsp::Position {
 9356                                line: params.text_document_position.position.line,
 9357                                character: params.text_document_position.position.character,
 9358                            },
 9359                            end: lsp::Position {
 9360                                line: params.text_document_position.position.line,
 9361                                character: params.text_document_position.position.character,
 9362                            },
 9363                        },
 9364                        new_text: "new_text_3".to_string(),
 9365                    })),
 9366                    ..lsp::CompletionItem::default()
 9367                },
 9368                lsp::CompletionItem {
 9369                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9370                    detail: Some(
 9371                        "Details with many     spaces and \t but without newlines".to_string(),
 9372                    ),
 9373                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9374                        range: lsp::Range {
 9375                            start: lsp::Position {
 9376                                line: params.text_document_position.position.line,
 9377                                character: params.text_document_position.position.character,
 9378                            },
 9379                            end: lsp::Position {
 9380                                line: params.text_document_position.position.line,
 9381                                character: params.text_document_position.position.character,
 9382                            },
 9383                        },
 9384                        new_text: "new_text_4".to_string(),
 9385                    })),
 9386                    ..lsp::CompletionItem::default()
 9387                },
 9388            ])))
 9389        });
 9390
 9391    editor.update_in(cx, |editor, window, cx| {
 9392        cx.focus_self(window);
 9393        editor.move_to_end(&MoveToEnd, window, cx);
 9394        editor.handle_input(".", window, cx);
 9395    });
 9396    cx.run_until_parked();
 9397    completion_handle.next().await.unwrap();
 9398
 9399    editor.update(cx, |editor, _| {
 9400        assert!(editor.context_menu_visible());
 9401        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9402        {
 9403            let completion_labels = menu
 9404                .completions
 9405                .borrow()
 9406                .iter()
 9407                .map(|c| c.label.text.clone())
 9408                .collect::<Vec<_>>();
 9409            assert_eq!(
 9410                completion_labels,
 9411                &[
 9412                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9413                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9414                    "single line label 2 d e f ",
 9415                    "a b c g h i ",
 9416                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9417                ],
 9418                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9419            );
 9420
 9421            for completion in menu
 9422                .completions
 9423                .borrow()
 9424                .iter() {
 9425                    assert_eq!(
 9426                        completion.label.filter_range,
 9427                        0..completion.label.text.len(),
 9428                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9429                    );
 9430                }
 9431
 9432        } else {
 9433            panic!("expected completion menu to be open");
 9434        }
 9435    });
 9436}
 9437
 9438#[gpui::test]
 9439async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9440    init_test(cx, |_| {});
 9441    let mut cx = EditorLspTestContext::new_rust(
 9442        lsp::ServerCapabilities {
 9443            completion_provider: Some(lsp::CompletionOptions {
 9444                trigger_characters: Some(vec![".".to_string()]),
 9445                ..Default::default()
 9446            }),
 9447            ..Default::default()
 9448        },
 9449        cx,
 9450    )
 9451    .await;
 9452    cx.lsp
 9453        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9454            Ok(Some(lsp::CompletionResponse::Array(vec![
 9455                lsp::CompletionItem {
 9456                    label: "first".into(),
 9457                    ..Default::default()
 9458                },
 9459                lsp::CompletionItem {
 9460                    label: "last".into(),
 9461                    ..Default::default()
 9462                },
 9463            ])))
 9464        });
 9465    cx.set_state("variableˇ");
 9466    cx.simulate_keystroke(".");
 9467    cx.executor().run_until_parked();
 9468
 9469    cx.update_editor(|editor, _, _| {
 9470        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9471        {
 9472            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9473        } else {
 9474            panic!("expected completion menu to be open");
 9475        }
 9476    });
 9477
 9478    cx.update_editor(|editor, window, cx| {
 9479        editor.move_page_down(&MovePageDown::default(), window, cx);
 9480        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9481        {
 9482            assert!(
 9483                menu.selected_item == 1,
 9484                "expected PageDown to select the last item from the context menu"
 9485            );
 9486        } else {
 9487            panic!("expected completion menu to stay open after PageDown");
 9488        }
 9489    });
 9490
 9491    cx.update_editor(|editor, window, cx| {
 9492        editor.move_page_up(&MovePageUp::default(), window, cx);
 9493        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9494        {
 9495            assert!(
 9496                menu.selected_item == 0,
 9497                "expected PageUp to select the first item from the context menu"
 9498            );
 9499        } else {
 9500            panic!("expected completion menu to stay open after PageUp");
 9501        }
 9502    });
 9503}
 9504
 9505#[gpui::test]
 9506async fn test_completion_sort(cx: &mut TestAppContext) {
 9507    init_test(cx, |_| {});
 9508    let mut cx = EditorLspTestContext::new_rust(
 9509        lsp::ServerCapabilities {
 9510            completion_provider: Some(lsp::CompletionOptions {
 9511                trigger_characters: Some(vec![".".to_string()]),
 9512                ..Default::default()
 9513            }),
 9514            ..Default::default()
 9515        },
 9516        cx,
 9517    )
 9518    .await;
 9519    cx.lsp
 9520        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9521            Ok(Some(lsp::CompletionResponse::Array(vec![
 9522                lsp::CompletionItem {
 9523                    label: "Range".into(),
 9524                    sort_text: Some("a".into()),
 9525                    ..Default::default()
 9526                },
 9527                lsp::CompletionItem {
 9528                    label: "r".into(),
 9529                    sort_text: Some("b".into()),
 9530                    ..Default::default()
 9531                },
 9532                lsp::CompletionItem {
 9533                    label: "ret".into(),
 9534                    sort_text: Some("c".into()),
 9535                    ..Default::default()
 9536                },
 9537                lsp::CompletionItem {
 9538                    label: "return".into(),
 9539                    sort_text: Some("d".into()),
 9540                    ..Default::default()
 9541                },
 9542                lsp::CompletionItem {
 9543                    label: "slice".into(),
 9544                    sort_text: Some("d".into()),
 9545                    ..Default::default()
 9546                },
 9547            ])))
 9548        });
 9549    cx.set_state("");
 9550    cx.executor().run_until_parked();
 9551    cx.update_editor(|editor, window, cx| {
 9552        editor.show_completions(
 9553            &ShowCompletions {
 9554                trigger: Some("r".into()),
 9555            },
 9556            window,
 9557            cx,
 9558        );
 9559    });
 9560    cx.executor().run_until_parked();
 9561
 9562    cx.update_editor(|editor, _, _| {
 9563        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9564        {
 9565            assert_eq!(
 9566                completion_menu_entries(&menu),
 9567                &["r", "ret", "Range", "return"]
 9568            );
 9569        } else {
 9570            panic!("expected completion menu to be open");
 9571        }
 9572    });
 9573}
 9574
 9575#[gpui::test]
 9576async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9577    init_test(cx, |_| {});
 9578
 9579    let mut cx = EditorLspTestContext::new_rust(
 9580        lsp::ServerCapabilities {
 9581            completion_provider: Some(lsp::CompletionOptions {
 9582                trigger_characters: Some(vec![".".to_string()]),
 9583                resolve_provider: Some(true),
 9584                ..Default::default()
 9585            }),
 9586            ..Default::default()
 9587        },
 9588        cx,
 9589    )
 9590    .await;
 9591
 9592    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9593    cx.simulate_keystroke(".");
 9594    let completion_item = lsp::CompletionItem {
 9595        label: "Some".into(),
 9596        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9597        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9598        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9599            kind: lsp::MarkupKind::Markdown,
 9600            value: "```rust\nSome(2)\n```".to_string(),
 9601        })),
 9602        deprecated: Some(false),
 9603        sort_text: Some("Some".to_string()),
 9604        filter_text: Some("Some".to_string()),
 9605        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9606        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9607            range: lsp::Range {
 9608                start: lsp::Position {
 9609                    line: 0,
 9610                    character: 22,
 9611                },
 9612                end: lsp::Position {
 9613                    line: 0,
 9614                    character: 22,
 9615                },
 9616            },
 9617            new_text: "Some(2)".to_string(),
 9618        })),
 9619        additional_text_edits: Some(vec![lsp::TextEdit {
 9620            range: lsp::Range {
 9621                start: lsp::Position {
 9622                    line: 0,
 9623                    character: 20,
 9624                },
 9625                end: lsp::Position {
 9626                    line: 0,
 9627                    character: 22,
 9628                },
 9629            },
 9630            new_text: "".to_string(),
 9631        }]),
 9632        ..Default::default()
 9633    };
 9634
 9635    let closure_completion_item = completion_item.clone();
 9636    let counter = Arc::new(AtomicUsize::new(0));
 9637    let counter_clone = counter.clone();
 9638    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9639        let task_completion_item = closure_completion_item.clone();
 9640        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9641        async move {
 9642            Ok(Some(lsp::CompletionResponse::Array(vec![
 9643                task_completion_item,
 9644            ])))
 9645        }
 9646    });
 9647
 9648    cx.condition(|editor, _| editor.context_menu_visible())
 9649        .await;
 9650    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9651    assert!(request.next().await.is_some());
 9652    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9653
 9654    cx.simulate_keystroke("S");
 9655    cx.simulate_keystroke("o");
 9656    cx.simulate_keystroke("m");
 9657    cx.condition(|editor, _| editor.context_menu_visible())
 9658        .await;
 9659    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9660    assert!(request.next().await.is_some());
 9661    assert!(request.next().await.is_some());
 9662    assert!(request.next().await.is_some());
 9663    request.close();
 9664    assert!(request.next().await.is_none());
 9665    assert_eq!(
 9666        counter.load(atomic::Ordering::Acquire),
 9667        4,
 9668        "With the completions menu open, only one LSP request should happen per input"
 9669    );
 9670}
 9671
 9672#[gpui::test]
 9673async fn test_toggle_comment(cx: &mut TestAppContext) {
 9674    init_test(cx, |_| {});
 9675    let mut cx = EditorTestContext::new(cx).await;
 9676    let language = Arc::new(Language::new(
 9677        LanguageConfig {
 9678            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9679            ..Default::default()
 9680        },
 9681        Some(tree_sitter_rust::LANGUAGE.into()),
 9682    ));
 9683    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9684
 9685    // If multiple selections intersect a line, the line is only toggled once.
 9686    cx.set_state(indoc! {"
 9687        fn a() {
 9688            «//b();
 9689            ˇ»// «c();
 9690            //ˇ»  d();
 9691        }
 9692    "});
 9693
 9694    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9695
 9696    cx.assert_editor_state(indoc! {"
 9697        fn a() {
 9698            «b();
 9699            c();
 9700            ˇ» d();
 9701        }
 9702    "});
 9703
 9704    // The comment prefix is inserted at the same column for every line in a
 9705    // selection.
 9706    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9707
 9708    cx.assert_editor_state(indoc! {"
 9709        fn a() {
 9710            // «b();
 9711            // c();
 9712            ˇ»//  d();
 9713        }
 9714    "});
 9715
 9716    // If a selection ends at the beginning of a line, that line is not toggled.
 9717    cx.set_selections_state(indoc! {"
 9718        fn a() {
 9719            // b();
 9720            «// c();
 9721        ˇ»    //  d();
 9722        }
 9723    "});
 9724
 9725    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9726
 9727    cx.assert_editor_state(indoc! {"
 9728        fn a() {
 9729            // b();
 9730            «c();
 9731        ˇ»    //  d();
 9732        }
 9733    "});
 9734
 9735    // If a selection span a single line and is empty, the line is toggled.
 9736    cx.set_state(indoc! {"
 9737        fn a() {
 9738            a();
 9739            b();
 9740        ˇ
 9741        }
 9742    "});
 9743
 9744    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9745
 9746    cx.assert_editor_state(indoc! {"
 9747        fn a() {
 9748            a();
 9749            b();
 9750        //•ˇ
 9751        }
 9752    "});
 9753
 9754    // If a selection span multiple lines, empty lines are not toggled.
 9755    cx.set_state(indoc! {"
 9756        fn a() {
 9757            «a();
 9758
 9759            c();ˇ»
 9760        }
 9761    "});
 9762
 9763    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9764
 9765    cx.assert_editor_state(indoc! {"
 9766        fn a() {
 9767            // «a();
 9768
 9769            // c();ˇ»
 9770        }
 9771    "});
 9772
 9773    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9774    cx.set_state(indoc! {"
 9775        fn a() {
 9776            «// a();
 9777            /// b();
 9778            //! c();ˇ»
 9779        }
 9780    "});
 9781
 9782    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9783
 9784    cx.assert_editor_state(indoc! {"
 9785        fn a() {
 9786            «a();
 9787            b();
 9788            c();ˇ»
 9789        }
 9790    "});
 9791}
 9792
 9793#[gpui::test]
 9794async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9795    init_test(cx, |_| {});
 9796    let mut cx = EditorTestContext::new(cx).await;
 9797    let language = Arc::new(Language::new(
 9798        LanguageConfig {
 9799            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9800            ..Default::default()
 9801        },
 9802        Some(tree_sitter_rust::LANGUAGE.into()),
 9803    ));
 9804    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9805
 9806    let toggle_comments = &ToggleComments {
 9807        advance_downwards: false,
 9808        ignore_indent: true,
 9809    };
 9810
 9811    // If multiple selections intersect a line, the line is only toggled once.
 9812    cx.set_state(indoc! {"
 9813        fn a() {
 9814        //    «b();
 9815        //    c();
 9816        //    ˇ» d();
 9817        }
 9818    "});
 9819
 9820    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9821
 9822    cx.assert_editor_state(indoc! {"
 9823        fn a() {
 9824            «b();
 9825            c();
 9826            ˇ» d();
 9827        }
 9828    "});
 9829
 9830    // The comment prefix is inserted at the beginning of each line
 9831    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9832
 9833    cx.assert_editor_state(indoc! {"
 9834        fn a() {
 9835        //    «b();
 9836        //    c();
 9837        //    ˇ» d();
 9838        }
 9839    "});
 9840
 9841    // If a selection ends at the beginning of a line, that line is not toggled.
 9842    cx.set_selections_state(indoc! {"
 9843        fn a() {
 9844        //    b();
 9845        //    «c();
 9846        ˇ»//     d();
 9847        }
 9848    "});
 9849
 9850    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9851
 9852    cx.assert_editor_state(indoc! {"
 9853        fn a() {
 9854        //    b();
 9855            «c();
 9856        ˇ»//     d();
 9857        }
 9858    "});
 9859
 9860    // If a selection span a single line and is empty, the line is toggled.
 9861    cx.set_state(indoc! {"
 9862        fn a() {
 9863            a();
 9864            b();
 9865        ˇ
 9866        }
 9867    "});
 9868
 9869    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9870
 9871    cx.assert_editor_state(indoc! {"
 9872        fn a() {
 9873            a();
 9874            b();
 9875        //ˇ
 9876        }
 9877    "});
 9878
 9879    // If a selection span multiple lines, empty lines are not toggled.
 9880    cx.set_state(indoc! {"
 9881        fn a() {
 9882            «a();
 9883
 9884            c();ˇ»
 9885        }
 9886    "});
 9887
 9888    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9889
 9890    cx.assert_editor_state(indoc! {"
 9891        fn a() {
 9892        //    «a();
 9893
 9894        //    c();ˇ»
 9895        }
 9896    "});
 9897
 9898    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9899    cx.set_state(indoc! {"
 9900        fn a() {
 9901        //    «a();
 9902        ///    b();
 9903        //!    c();ˇ»
 9904        }
 9905    "});
 9906
 9907    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9908
 9909    cx.assert_editor_state(indoc! {"
 9910        fn a() {
 9911            «a();
 9912            b();
 9913            c();ˇ»
 9914        }
 9915    "});
 9916}
 9917
 9918#[gpui::test]
 9919async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9920    init_test(cx, |_| {});
 9921
 9922    let language = Arc::new(Language::new(
 9923        LanguageConfig {
 9924            line_comments: vec!["// ".into()],
 9925            ..Default::default()
 9926        },
 9927        Some(tree_sitter_rust::LANGUAGE.into()),
 9928    ));
 9929
 9930    let mut cx = EditorTestContext::new(cx).await;
 9931
 9932    cx.language_registry().add(language.clone());
 9933    cx.update_buffer(|buffer, cx| {
 9934        buffer.set_language(Some(language), cx);
 9935    });
 9936
 9937    let toggle_comments = &ToggleComments {
 9938        advance_downwards: true,
 9939        ignore_indent: false,
 9940    };
 9941
 9942    // Single cursor on one line -> advance
 9943    // Cursor moves horizontally 3 characters as well on non-blank line
 9944    cx.set_state(indoc!(
 9945        "fn a() {
 9946             ˇdog();
 9947             cat();
 9948        }"
 9949    ));
 9950    cx.update_editor(|editor, window, cx| {
 9951        editor.toggle_comments(toggle_comments, window, cx);
 9952    });
 9953    cx.assert_editor_state(indoc!(
 9954        "fn a() {
 9955             // dog();
 9956             catˇ();
 9957        }"
 9958    ));
 9959
 9960    // Single selection on one line -> don't advance
 9961    cx.set_state(indoc!(
 9962        "fn a() {
 9963             «dog()ˇ»;
 9964             cat();
 9965        }"
 9966    ));
 9967    cx.update_editor(|editor, window, cx| {
 9968        editor.toggle_comments(toggle_comments, window, cx);
 9969    });
 9970    cx.assert_editor_state(indoc!(
 9971        "fn a() {
 9972             // «dog()ˇ»;
 9973             cat();
 9974        }"
 9975    ));
 9976
 9977    // Multiple cursors on one line -> advance
 9978    cx.set_state(indoc!(
 9979        "fn a() {
 9980             ˇdˇog();
 9981             cat();
 9982        }"
 9983    ));
 9984    cx.update_editor(|editor, window, cx| {
 9985        editor.toggle_comments(toggle_comments, window, cx);
 9986    });
 9987    cx.assert_editor_state(indoc!(
 9988        "fn a() {
 9989             // dog();
 9990             catˇ(ˇ);
 9991        }"
 9992    ));
 9993
 9994    // Multiple cursors on one line, with selection -> don't advance
 9995    cx.set_state(indoc!(
 9996        "fn a() {
 9997             ˇdˇog«()ˇ»;
 9998             cat();
 9999        }"
10000    ));
10001    cx.update_editor(|editor, window, cx| {
10002        editor.toggle_comments(toggle_comments, window, cx);
10003    });
10004    cx.assert_editor_state(indoc!(
10005        "fn a() {
10006             // ˇdˇog«()ˇ»;
10007             cat();
10008        }"
10009    ));
10010
10011    // Single cursor on one line -> advance
10012    // Cursor moves to column 0 on blank line
10013    cx.set_state(indoc!(
10014        "fn a() {
10015             ˇdog();
10016
10017             cat();
10018        }"
10019    ));
10020    cx.update_editor(|editor, window, cx| {
10021        editor.toggle_comments(toggle_comments, window, cx);
10022    });
10023    cx.assert_editor_state(indoc!(
10024        "fn a() {
10025             // dog();
10026        ˇ
10027             cat();
10028        }"
10029    ));
10030
10031    // Single cursor on one line -> advance
10032    // Cursor starts and ends at column 0
10033    cx.set_state(indoc!(
10034        "fn a() {
10035         ˇ    dog();
10036             cat();
10037        }"
10038    ));
10039    cx.update_editor(|editor, window, cx| {
10040        editor.toggle_comments(toggle_comments, window, cx);
10041    });
10042    cx.assert_editor_state(indoc!(
10043        "fn a() {
10044             // dog();
10045         ˇ    cat();
10046        }"
10047    ));
10048}
10049
10050#[gpui::test]
10051async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10052    init_test(cx, |_| {});
10053
10054    let mut cx = EditorTestContext::new(cx).await;
10055
10056    let html_language = Arc::new(
10057        Language::new(
10058            LanguageConfig {
10059                name: "HTML".into(),
10060                block_comment: Some(("<!-- ".into(), " -->".into())),
10061                ..Default::default()
10062            },
10063            Some(tree_sitter_html::LANGUAGE.into()),
10064        )
10065        .with_injection_query(
10066            r#"
10067            (script_element
10068                (raw_text) @injection.content
10069                (#set! injection.language "javascript"))
10070            "#,
10071        )
10072        .unwrap(),
10073    );
10074
10075    let javascript_language = Arc::new(Language::new(
10076        LanguageConfig {
10077            name: "JavaScript".into(),
10078            line_comments: vec!["// ".into()],
10079            ..Default::default()
10080        },
10081        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10082    ));
10083
10084    cx.language_registry().add(html_language.clone());
10085    cx.language_registry().add(javascript_language.clone());
10086    cx.update_buffer(|buffer, cx| {
10087        buffer.set_language(Some(html_language), cx);
10088    });
10089
10090    // Toggle comments for empty selections
10091    cx.set_state(
10092        &r#"
10093            <p>A</p>ˇ
10094            <p>B</p>ˇ
10095            <p>C</p>ˇ
10096        "#
10097        .unindent(),
10098    );
10099    cx.update_editor(|editor, window, cx| {
10100        editor.toggle_comments(&ToggleComments::default(), window, cx)
10101    });
10102    cx.assert_editor_state(
10103        &r#"
10104            <!-- <p>A</p>ˇ -->
10105            <!-- <p>B</p>ˇ -->
10106            <!-- <p>C</p>ˇ -->
10107        "#
10108        .unindent(),
10109    );
10110    cx.update_editor(|editor, window, cx| {
10111        editor.toggle_comments(&ToggleComments::default(), window, cx)
10112    });
10113    cx.assert_editor_state(
10114        &r#"
10115            <p>A</p>ˇ
10116            <p>B</p>ˇ
10117            <p>C</p>ˇ
10118        "#
10119        .unindent(),
10120    );
10121
10122    // Toggle comments for mixture of empty and non-empty selections, where
10123    // multiple selections occupy a given line.
10124    cx.set_state(
10125        &r#"
10126            <p>A«</p>
10127            <p>ˇ»B</p>ˇ
10128            <p>C«</p>
10129            <p>ˇ»D</p>ˇ
10130        "#
10131        .unindent(),
10132    );
10133
10134    cx.update_editor(|editor, window, cx| {
10135        editor.toggle_comments(&ToggleComments::default(), window, cx)
10136    });
10137    cx.assert_editor_state(
10138        &r#"
10139            <!-- <p>A«</p>
10140            <p>ˇ»B</p>ˇ -->
10141            <!-- <p>C«</p>
10142            <p>ˇ»D</p>ˇ -->
10143        "#
10144        .unindent(),
10145    );
10146    cx.update_editor(|editor, window, cx| {
10147        editor.toggle_comments(&ToggleComments::default(), window, cx)
10148    });
10149    cx.assert_editor_state(
10150        &r#"
10151            <p>A«</p>
10152            <p>ˇ»B</p>ˇ
10153            <p>C«</p>
10154            <p>ˇ»D</p>ˇ
10155        "#
10156        .unindent(),
10157    );
10158
10159    // Toggle comments when different languages are active for different
10160    // selections.
10161    cx.set_state(
10162        &r#"
10163            ˇ<script>
10164                ˇvar x = new Y();
10165            ˇ</script>
10166        "#
10167        .unindent(),
10168    );
10169    cx.executor().run_until_parked();
10170    cx.update_editor(|editor, window, cx| {
10171        editor.toggle_comments(&ToggleComments::default(), window, cx)
10172    });
10173    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10174    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10175    cx.assert_editor_state(
10176        &r#"
10177            <!-- ˇ<script> -->
10178                // ˇvar x = new Y();
10179            <!-- ˇ</script> -->
10180        "#
10181        .unindent(),
10182    );
10183}
10184
10185#[gpui::test]
10186fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10187    init_test(cx, |_| {});
10188
10189    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10190    let multibuffer = cx.new(|cx| {
10191        let mut multibuffer = MultiBuffer::new(ReadWrite);
10192        multibuffer.push_excerpts(
10193            buffer.clone(),
10194            [
10195                ExcerptRange {
10196                    context: Point::new(0, 0)..Point::new(0, 4),
10197                    primary: None,
10198                },
10199                ExcerptRange {
10200                    context: Point::new(1, 0)..Point::new(1, 4),
10201                    primary: None,
10202                },
10203            ],
10204            cx,
10205        );
10206        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10207        multibuffer
10208    });
10209
10210    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10211    editor.update_in(cx, |editor, window, cx| {
10212        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10213        editor.change_selections(None, window, cx, |s| {
10214            s.select_ranges([
10215                Point::new(0, 0)..Point::new(0, 0),
10216                Point::new(1, 0)..Point::new(1, 0),
10217            ])
10218        });
10219
10220        editor.handle_input("X", window, cx);
10221        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10222        assert_eq!(
10223            editor.selections.ranges(cx),
10224            [
10225                Point::new(0, 1)..Point::new(0, 1),
10226                Point::new(1, 1)..Point::new(1, 1),
10227            ]
10228        );
10229
10230        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10231        editor.change_selections(None, window, cx, |s| {
10232            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10233        });
10234        editor.backspace(&Default::default(), window, cx);
10235        assert_eq!(editor.text(cx), "Xa\nbbb");
10236        assert_eq!(
10237            editor.selections.ranges(cx),
10238            [Point::new(1, 0)..Point::new(1, 0)]
10239        );
10240
10241        editor.change_selections(None, window, cx, |s| {
10242            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10243        });
10244        editor.backspace(&Default::default(), window, cx);
10245        assert_eq!(editor.text(cx), "X\nbb");
10246        assert_eq!(
10247            editor.selections.ranges(cx),
10248            [Point::new(0, 1)..Point::new(0, 1)]
10249        );
10250    });
10251}
10252
10253#[gpui::test]
10254fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10255    init_test(cx, |_| {});
10256
10257    let markers = vec![('[', ']').into(), ('(', ')').into()];
10258    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10259        indoc! {"
10260            [aaaa
10261            (bbbb]
10262            cccc)",
10263        },
10264        markers.clone(),
10265    );
10266    let excerpt_ranges = markers.into_iter().map(|marker| {
10267        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10268        ExcerptRange {
10269            context,
10270            primary: None,
10271        }
10272    });
10273    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10274    let multibuffer = cx.new(|cx| {
10275        let mut multibuffer = MultiBuffer::new(ReadWrite);
10276        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10277        multibuffer
10278    });
10279
10280    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10281    editor.update_in(cx, |editor, window, cx| {
10282        let (expected_text, selection_ranges) = marked_text_ranges(
10283            indoc! {"
10284                aaaa
10285                bˇbbb
10286                bˇbbˇb
10287                cccc"
10288            },
10289            true,
10290        );
10291        assert_eq!(editor.text(cx), expected_text);
10292        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10293
10294        editor.handle_input("X", window, cx);
10295
10296        let (expected_text, expected_selections) = marked_text_ranges(
10297            indoc! {"
10298                aaaa
10299                bXˇbbXb
10300                bXˇbbXˇb
10301                cccc"
10302            },
10303            false,
10304        );
10305        assert_eq!(editor.text(cx), expected_text);
10306        assert_eq!(editor.selections.ranges(cx), expected_selections);
10307
10308        editor.newline(&Newline, window, cx);
10309        let (expected_text, expected_selections) = marked_text_ranges(
10310            indoc! {"
10311                aaaa
10312                bX
10313                ˇbbX
10314                b
10315                bX
10316                ˇbbX
10317                ˇb
10318                cccc"
10319            },
10320            false,
10321        );
10322        assert_eq!(editor.text(cx), expected_text);
10323        assert_eq!(editor.selections.ranges(cx), expected_selections);
10324    });
10325}
10326
10327#[gpui::test]
10328fn test_refresh_selections(cx: &mut TestAppContext) {
10329    init_test(cx, |_| {});
10330
10331    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10332    let mut excerpt1_id = None;
10333    let multibuffer = cx.new(|cx| {
10334        let mut multibuffer = MultiBuffer::new(ReadWrite);
10335        excerpt1_id = multibuffer
10336            .push_excerpts(
10337                buffer.clone(),
10338                [
10339                    ExcerptRange {
10340                        context: Point::new(0, 0)..Point::new(1, 4),
10341                        primary: None,
10342                    },
10343                    ExcerptRange {
10344                        context: Point::new(1, 0)..Point::new(2, 4),
10345                        primary: None,
10346                    },
10347                ],
10348                cx,
10349            )
10350            .into_iter()
10351            .next();
10352        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10353        multibuffer
10354    });
10355
10356    let editor = cx.add_window(|window, cx| {
10357        let mut editor = build_editor(multibuffer.clone(), window, cx);
10358        let snapshot = editor.snapshot(window, cx);
10359        editor.change_selections(None, window, cx, |s| {
10360            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10361        });
10362        editor.begin_selection(
10363            Point::new(2, 1).to_display_point(&snapshot),
10364            true,
10365            1,
10366            window,
10367            cx,
10368        );
10369        assert_eq!(
10370            editor.selections.ranges(cx),
10371            [
10372                Point::new(1, 3)..Point::new(1, 3),
10373                Point::new(2, 1)..Point::new(2, 1),
10374            ]
10375        );
10376        editor
10377    });
10378
10379    // Refreshing selections is a no-op when excerpts haven't changed.
10380    _ = editor.update(cx, |editor, window, cx| {
10381        editor.change_selections(None, window, cx, |s| s.refresh());
10382        assert_eq!(
10383            editor.selections.ranges(cx),
10384            [
10385                Point::new(1, 3)..Point::new(1, 3),
10386                Point::new(2, 1)..Point::new(2, 1),
10387            ]
10388        );
10389    });
10390
10391    multibuffer.update(cx, |multibuffer, cx| {
10392        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10393    });
10394    _ = editor.update(cx, |editor, window, cx| {
10395        // Removing an excerpt causes the first selection to become degenerate.
10396        assert_eq!(
10397            editor.selections.ranges(cx),
10398            [
10399                Point::new(0, 0)..Point::new(0, 0),
10400                Point::new(0, 1)..Point::new(0, 1)
10401            ]
10402        );
10403
10404        // Refreshing selections will relocate the first selection to the original buffer
10405        // location.
10406        editor.change_selections(None, window, cx, |s| s.refresh());
10407        assert_eq!(
10408            editor.selections.ranges(cx),
10409            [
10410                Point::new(0, 1)..Point::new(0, 1),
10411                Point::new(0, 3)..Point::new(0, 3)
10412            ]
10413        );
10414        assert!(editor.selections.pending_anchor().is_some());
10415    });
10416}
10417
10418#[gpui::test]
10419fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10420    init_test(cx, |_| {});
10421
10422    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10423    let mut excerpt1_id = None;
10424    let multibuffer = cx.new(|cx| {
10425        let mut multibuffer = MultiBuffer::new(ReadWrite);
10426        excerpt1_id = multibuffer
10427            .push_excerpts(
10428                buffer.clone(),
10429                [
10430                    ExcerptRange {
10431                        context: Point::new(0, 0)..Point::new(1, 4),
10432                        primary: None,
10433                    },
10434                    ExcerptRange {
10435                        context: Point::new(1, 0)..Point::new(2, 4),
10436                        primary: None,
10437                    },
10438                ],
10439                cx,
10440            )
10441            .into_iter()
10442            .next();
10443        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10444        multibuffer
10445    });
10446
10447    let editor = cx.add_window(|window, cx| {
10448        let mut editor = build_editor(multibuffer.clone(), window, cx);
10449        let snapshot = editor.snapshot(window, cx);
10450        editor.begin_selection(
10451            Point::new(1, 3).to_display_point(&snapshot),
10452            false,
10453            1,
10454            window,
10455            cx,
10456        );
10457        assert_eq!(
10458            editor.selections.ranges(cx),
10459            [Point::new(1, 3)..Point::new(1, 3)]
10460        );
10461        editor
10462    });
10463
10464    multibuffer.update(cx, |multibuffer, cx| {
10465        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10466    });
10467    _ = editor.update(cx, |editor, window, cx| {
10468        assert_eq!(
10469            editor.selections.ranges(cx),
10470            [Point::new(0, 0)..Point::new(0, 0)]
10471        );
10472
10473        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10474        editor.change_selections(None, window, cx, |s| s.refresh());
10475        assert_eq!(
10476            editor.selections.ranges(cx),
10477            [Point::new(0, 3)..Point::new(0, 3)]
10478        );
10479        assert!(editor.selections.pending_anchor().is_some());
10480    });
10481}
10482
10483#[gpui::test]
10484async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10485    init_test(cx, |_| {});
10486
10487    let language = Arc::new(
10488        Language::new(
10489            LanguageConfig {
10490                brackets: BracketPairConfig {
10491                    pairs: vec![
10492                        BracketPair {
10493                            start: "{".to_string(),
10494                            end: "}".to_string(),
10495                            close: true,
10496                            surround: true,
10497                            newline: true,
10498                        },
10499                        BracketPair {
10500                            start: "/* ".to_string(),
10501                            end: " */".to_string(),
10502                            close: true,
10503                            surround: true,
10504                            newline: true,
10505                        },
10506                    ],
10507                    ..Default::default()
10508                },
10509                ..Default::default()
10510            },
10511            Some(tree_sitter_rust::LANGUAGE.into()),
10512        )
10513        .with_indents_query("")
10514        .unwrap(),
10515    );
10516
10517    let text = concat!(
10518        "{   }\n",     //
10519        "  x\n",       //
10520        "  /*   */\n", //
10521        "x\n",         //
10522        "{{} }\n",     //
10523    );
10524
10525    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10526    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10527    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10528    editor
10529        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10530        .await;
10531
10532    editor.update_in(cx, |editor, window, cx| {
10533        editor.change_selections(None, window, cx, |s| {
10534            s.select_display_ranges([
10535                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10536                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10537                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10538            ])
10539        });
10540        editor.newline(&Newline, window, cx);
10541
10542        assert_eq!(
10543            editor.buffer().read(cx).read(cx).text(),
10544            concat!(
10545                "{ \n",    // Suppress rustfmt
10546                "\n",      //
10547                "}\n",     //
10548                "  x\n",   //
10549                "  /* \n", //
10550                "  \n",    //
10551                "  */\n",  //
10552                "x\n",     //
10553                "{{} \n",  //
10554                "}\n",     //
10555            )
10556        );
10557    });
10558}
10559
10560#[gpui::test]
10561fn test_highlighted_ranges(cx: &mut TestAppContext) {
10562    init_test(cx, |_| {});
10563
10564    let editor = cx.add_window(|window, cx| {
10565        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10566        build_editor(buffer.clone(), window, cx)
10567    });
10568
10569    _ = editor.update(cx, |editor, window, cx| {
10570        struct Type1;
10571        struct Type2;
10572
10573        let buffer = editor.buffer.read(cx).snapshot(cx);
10574
10575        let anchor_range =
10576            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10577
10578        editor.highlight_background::<Type1>(
10579            &[
10580                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10581                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10582                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10583                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10584            ],
10585            |_| Hsla::red(),
10586            cx,
10587        );
10588        editor.highlight_background::<Type2>(
10589            &[
10590                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10591                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10592                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10593                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10594            ],
10595            |_| Hsla::green(),
10596            cx,
10597        );
10598
10599        let snapshot = editor.snapshot(window, cx);
10600        let mut highlighted_ranges = editor.background_highlights_in_range(
10601            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10602            &snapshot,
10603            cx.theme().colors(),
10604        );
10605        // Enforce a consistent ordering based on color without relying on the ordering of the
10606        // highlight's `TypeId` which is non-executor.
10607        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10608        assert_eq!(
10609            highlighted_ranges,
10610            &[
10611                (
10612                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10613                    Hsla::red(),
10614                ),
10615                (
10616                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10617                    Hsla::red(),
10618                ),
10619                (
10620                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10621                    Hsla::green(),
10622                ),
10623                (
10624                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10625                    Hsla::green(),
10626                ),
10627            ]
10628        );
10629        assert_eq!(
10630            editor.background_highlights_in_range(
10631                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10632                &snapshot,
10633                cx.theme().colors(),
10634            ),
10635            &[(
10636                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10637                Hsla::red(),
10638            )]
10639        );
10640    });
10641}
10642
10643#[gpui::test]
10644async fn test_following(cx: &mut TestAppContext) {
10645    init_test(cx, |_| {});
10646
10647    let fs = FakeFs::new(cx.executor());
10648    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10649
10650    let buffer = project.update(cx, |project, cx| {
10651        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10652        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10653    });
10654    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10655    let follower = cx.update(|cx| {
10656        cx.open_window(
10657            WindowOptions {
10658                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10659                    gpui::Point::new(px(0.), px(0.)),
10660                    gpui::Point::new(px(10.), px(80.)),
10661                ))),
10662                ..Default::default()
10663            },
10664            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10665        )
10666        .unwrap()
10667    });
10668
10669    let is_still_following = Rc::new(RefCell::new(true));
10670    let follower_edit_event_count = Rc::new(RefCell::new(0));
10671    let pending_update = Rc::new(RefCell::new(None));
10672    let leader_entity = leader.root(cx).unwrap();
10673    let follower_entity = follower.root(cx).unwrap();
10674    _ = follower.update(cx, {
10675        let update = pending_update.clone();
10676        let is_still_following = is_still_following.clone();
10677        let follower_edit_event_count = follower_edit_event_count.clone();
10678        |_, window, cx| {
10679            cx.subscribe_in(
10680                &leader_entity,
10681                window,
10682                move |_, leader, event, window, cx| {
10683                    leader.read(cx).add_event_to_update_proto(
10684                        event,
10685                        &mut update.borrow_mut(),
10686                        window,
10687                        cx,
10688                    );
10689                },
10690            )
10691            .detach();
10692
10693            cx.subscribe_in(
10694                &follower_entity,
10695                window,
10696                move |_, _, event: &EditorEvent, _window, _cx| {
10697                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10698                        *is_still_following.borrow_mut() = false;
10699                    }
10700
10701                    if let EditorEvent::BufferEdited = event {
10702                        *follower_edit_event_count.borrow_mut() += 1;
10703                    }
10704                },
10705            )
10706            .detach();
10707        }
10708    });
10709
10710    // Update the selections only
10711    _ = leader.update(cx, |leader, window, cx| {
10712        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10713    });
10714    follower
10715        .update(cx, |follower, window, cx| {
10716            follower.apply_update_proto(
10717                &project,
10718                pending_update.borrow_mut().take().unwrap(),
10719                window,
10720                cx,
10721            )
10722        })
10723        .unwrap()
10724        .await
10725        .unwrap();
10726    _ = follower.update(cx, |follower, _, cx| {
10727        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10728    });
10729    assert!(*is_still_following.borrow());
10730    assert_eq!(*follower_edit_event_count.borrow(), 0);
10731
10732    // Update the scroll position only
10733    _ = leader.update(cx, |leader, window, cx| {
10734        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10735    });
10736    follower
10737        .update(cx, |follower, window, cx| {
10738            follower.apply_update_proto(
10739                &project,
10740                pending_update.borrow_mut().take().unwrap(),
10741                window,
10742                cx,
10743            )
10744        })
10745        .unwrap()
10746        .await
10747        .unwrap();
10748    assert_eq!(
10749        follower
10750            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10751            .unwrap(),
10752        gpui::Point::new(1.5, 3.5)
10753    );
10754    assert!(*is_still_following.borrow());
10755    assert_eq!(*follower_edit_event_count.borrow(), 0);
10756
10757    // Update the selections and scroll position. The follower's scroll position is updated
10758    // via autoscroll, not via the leader's exact scroll position.
10759    _ = leader.update(cx, |leader, window, cx| {
10760        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10761        leader.request_autoscroll(Autoscroll::newest(), cx);
10762        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10763    });
10764    follower
10765        .update(cx, |follower, window, cx| {
10766            follower.apply_update_proto(
10767                &project,
10768                pending_update.borrow_mut().take().unwrap(),
10769                window,
10770                cx,
10771            )
10772        })
10773        .unwrap()
10774        .await
10775        .unwrap();
10776    _ = follower.update(cx, |follower, _, cx| {
10777        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10778        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10779    });
10780    assert!(*is_still_following.borrow());
10781
10782    // Creating a pending selection that precedes another selection
10783    _ = leader.update(cx, |leader, window, cx| {
10784        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10785        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10786    });
10787    follower
10788        .update(cx, |follower, window, cx| {
10789            follower.apply_update_proto(
10790                &project,
10791                pending_update.borrow_mut().take().unwrap(),
10792                window,
10793                cx,
10794            )
10795        })
10796        .unwrap()
10797        .await
10798        .unwrap();
10799    _ = follower.update(cx, |follower, _, cx| {
10800        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10801    });
10802    assert!(*is_still_following.borrow());
10803
10804    // Extend the pending selection so that it surrounds another selection
10805    _ = leader.update(cx, |leader, window, cx| {
10806        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10807    });
10808    follower
10809        .update(cx, |follower, window, cx| {
10810            follower.apply_update_proto(
10811                &project,
10812                pending_update.borrow_mut().take().unwrap(),
10813                window,
10814                cx,
10815            )
10816        })
10817        .unwrap()
10818        .await
10819        .unwrap();
10820    _ = follower.update(cx, |follower, _, cx| {
10821        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10822    });
10823
10824    // Scrolling locally breaks the follow
10825    _ = follower.update(cx, |follower, window, cx| {
10826        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10827        follower.set_scroll_anchor(
10828            ScrollAnchor {
10829                anchor: top_anchor,
10830                offset: gpui::Point::new(0.0, 0.5),
10831            },
10832            window,
10833            cx,
10834        );
10835    });
10836    assert!(!(*is_still_following.borrow()));
10837}
10838
10839#[gpui::test]
10840async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10841    init_test(cx, |_| {});
10842
10843    let fs = FakeFs::new(cx.executor());
10844    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10845    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10846    let pane = workspace
10847        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10848        .unwrap();
10849
10850    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10851
10852    let leader = pane.update_in(cx, |_, window, cx| {
10853        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10854        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10855    });
10856
10857    // Start following the editor when it has no excerpts.
10858    let mut state_message =
10859        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10860    let workspace_entity = workspace.root(cx).unwrap();
10861    let follower_1 = cx
10862        .update_window(*workspace.deref(), |_, window, cx| {
10863            Editor::from_state_proto(
10864                workspace_entity,
10865                ViewId {
10866                    creator: Default::default(),
10867                    id: 0,
10868                },
10869                &mut state_message,
10870                window,
10871                cx,
10872            )
10873        })
10874        .unwrap()
10875        .unwrap()
10876        .await
10877        .unwrap();
10878
10879    let update_message = Rc::new(RefCell::new(None));
10880    follower_1.update_in(cx, {
10881        let update = update_message.clone();
10882        |_, window, cx| {
10883            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10884                leader.read(cx).add_event_to_update_proto(
10885                    event,
10886                    &mut update.borrow_mut(),
10887                    window,
10888                    cx,
10889                );
10890            })
10891            .detach();
10892        }
10893    });
10894
10895    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10896        (
10897            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10898            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10899        )
10900    });
10901
10902    // Insert some excerpts.
10903    leader.update(cx, |leader, cx| {
10904        leader.buffer.update(cx, |multibuffer, cx| {
10905            let excerpt_ids = multibuffer.push_excerpts(
10906                buffer_1.clone(),
10907                [
10908                    ExcerptRange {
10909                        context: 1..6,
10910                        primary: None,
10911                    },
10912                    ExcerptRange {
10913                        context: 12..15,
10914                        primary: None,
10915                    },
10916                    ExcerptRange {
10917                        context: 0..3,
10918                        primary: None,
10919                    },
10920                ],
10921                cx,
10922            );
10923            multibuffer.insert_excerpts_after(
10924                excerpt_ids[0],
10925                buffer_2.clone(),
10926                [
10927                    ExcerptRange {
10928                        context: 8..12,
10929                        primary: None,
10930                    },
10931                    ExcerptRange {
10932                        context: 0..6,
10933                        primary: None,
10934                    },
10935                ],
10936                cx,
10937            );
10938        });
10939    });
10940
10941    // Apply the update of adding the excerpts.
10942    follower_1
10943        .update_in(cx, |follower, window, cx| {
10944            follower.apply_update_proto(
10945                &project,
10946                update_message.borrow().clone().unwrap(),
10947                window,
10948                cx,
10949            )
10950        })
10951        .await
10952        .unwrap();
10953    assert_eq!(
10954        follower_1.update(cx, |editor, cx| editor.text(cx)),
10955        leader.update(cx, |editor, cx| editor.text(cx))
10956    );
10957    update_message.borrow_mut().take();
10958
10959    // Start following separately after it already has excerpts.
10960    let mut state_message =
10961        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10962    let workspace_entity = workspace.root(cx).unwrap();
10963    let follower_2 = cx
10964        .update_window(*workspace.deref(), |_, window, cx| {
10965            Editor::from_state_proto(
10966                workspace_entity,
10967                ViewId {
10968                    creator: Default::default(),
10969                    id: 0,
10970                },
10971                &mut state_message,
10972                window,
10973                cx,
10974            )
10975        })
10976        .unwrap()
10977        .unwrap()
10978        .await
10979        .unwrap();
10980    assert_eq!(
10981        follower_2.update(cx, |editor, cx| editor.text(cx)),
10982        leader.update(cx, |editor, cx| editor.text(cx))
10983    );
10984
10985    // Remove some excerpts.
10986    leader.update(cx, |leader, cx| {
10987        leader.buffer.update(cx, |multibuffer, cx| {
10988            let excerpt_ids = multibuffer.excerpt_ids();
10989            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10990            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10991        });
10992    });
10993
10994    // Apply the update of removing the excerpts.
10995    follower_1
10996        .update_in(cx, |follower, window, cx| {
10997            follower.apply_update_proto(
10998                &project,
10999                update_message.borrow().clone().unwrap(),
11000                window,
11001                cx,
11002            )
11003        })
11004        .await
11005        .unwrap();
11006    follower_2
11007        .update_in(cx, |follower, window, cx| {
11008            follower.apply_update_proto(
11009                &project,
11010                update_message.borrow().clone().unwrap(),
11011                window,
11012                cx,
11013            )
11014        })
11015        .await
11016        .unwrap();
11017    update_message.borrow_mut().take();
11018    assert_eq!(
11019        follower_1.update(cx, |editor, cx| editor.text(cx)),
11020        leader.update(cx, |editor, cx| editor.text(cx))
11021    );
11022}
11023
11024#[gpui::test]
11025async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11026    init_test(cx, |_| {});
11027
11028    let mut cx = EditorTestContext::new(cx).await;
11029    let lsp_store =
11030        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11031
11032    cx.set_state(indoc! {"
11033        ˇfn func(abc def: i32) -> u32 {
11034        }
11035    "});
11036
11037    cx.update(|_, cx| {
11038        lsp_store.update(cx, |lsp_store, cx| {
11039            lsp_store
11040                .update_diagnostics(
11041                    LanguageServerId(0),
11042                    lsp::PublishDiagnosticsParams {
11043                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11044                        version: None,
11045                        diagnostics: vec![
11046                            lsp::Diagnostic {
11047                                range: lsp::Range::new(
11048                                    lsp::Position::new(0, 11),
11049                                    lsp::Position::new(0, 12),
11050                                ),
11051                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11052                                ..Default::default()
11053                            },
11054                            lsp::Diagnostic {
11055                                range: lsp::Range::new(
11056                                    lsp::Position::new(0, 12),
11057                                    lsp::Position::new(0, 15),
11058                                ),
11059                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11060                                ..Default::default()
11061                            },
11062                            lsp::Diagnostic {
11063                                range: lsp::Range::new(
11064                                    lsp::Position::new(0, 25),
11065                                    lsp::Position::new(0, 28),
11066                                ),
11067                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11068                                ..Default::default()
11069                            },
11070                        ],
11071                    },
11072                    &[],
11073                    cx,
11074                )
11075                .unwrap()
11076        });
11077    });
11078
11079    executor.run_until_parked();
11080
11081    cx.update_editor(|editor, window, cx| {
11082        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11083    });
11084
11085    cx.assert_editor_state(indoc! {"
11086        fn func(abc def: i32) -> ˇu32 {
11087        }
11088    "});
11089
11090    cx.update_editor(|editor, window, cx| {
11091        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11092    });
11093
11094    cx.assert_editor_state(indoc! {"
11095        fn func(abc ˇdef: i32) -> u32 {
11096        }
11097    "});
11098
11099    cx.update_editor(|editor, window, cx| {
11100        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11101    });
11102
11103    cx.assert_editor_state(indoc! {"
11104        fn func(abcˇ def: i32) -> u32 {
11105        }
11106    "});
11107
11108    cx.update_editor(|editor, window, cx| {
11109        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11110    });
11111
11112    cx.assert_editor_state(indoc! {"
11113        fn func(abc def: i32) -> ˇu32 {
11114        }
11115    "});
11116}
11117
11118#[gpui::test]
11119async fn cycle_through_same_place_diagnostics(
11120    executor: BackgroundExecutor,
11121    cx: &mut TestAppContext,
11122) {
11123    init_test(cx, |_| {});
11124
11125    let mut cx = EditorTestContext::new(cx).await;
11126    let lsp_store =
11127        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11128
11129    cx.set_state(indoc! {"
11130        ˇfn func(abc def: i32) -> u32 {
11131        }
11132    "});
11133
11134    cx.update(|_, cx| {
11135        lsp_store.update(cx, |lsp_store, cx| {
11136            lsp_store
11137                .update_diagnostics(
11138                    LanguageServerId(0),
11139                    lsp::PublishDiagnosticsParams {
11140                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11141                        version: None,
11142                        diagnostics: vec![
11143                            lsp::Diagnostic {
11144                                range: lsp::Range::new(
11145                                    lsp::Position::new(0, 11),
11146                                    lsp::Position::new(0, 12),
11147                                ),
11148                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11149                                ..Default::default()
11150                            },
11151                            lsp::Diagnostic {
11152                                range: lsp::Range::new(
11153                                    lsp::Position::new(0, 12),
11154                                    lsp::Position::new(0, 15),
11155                                ),
11156                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11157                                ..Default::default()
11158                            },
11159                            lsp::Diagnostic {
11160                                range: lsp::Range::new(
11161                                    lsp::Position::new(0, 12),
11162                                    lsp::Position::new(0, 15),
11163                                ),
11164                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11165                                ..Default::default()
11166                            },
11167                            lsp::Diagnostic {
11168                                range: lsp::Range::new(
11169                                    lsp::Position::new(0, 25),
11170                                    lsp::Position::new(0, 28),
11171                                ),
11172                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11173                                ..Default::default()
11174                            },
11175                        ],
11176                    },
11177                    &[],
11178                    cx,
11179                )
11180                .unwrap()
11181        });
11182    });
11183    executor.run_until_parked();
11184
11185    //// Backward
11186
11187    // Fourth diagnostic
11188    cx.update_editor(|editor, window, cx| {
11189        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11190    });
11191    cx.assert_editor_state(indoc! {"
11192        fn func(abc def: i32) -> ˇu32 {
11193        }
11194    "});
11195
11196    // Third diagnostic
11197    cx.update_editor(|editor, window, cx| {
11198        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11199    });
11200    cx.assert_editor_state(indoc! {"
11201        fn func(abc ˇdef: i32) -> u32 {
11202        }
11203    "});
11204
11205    // Second diagnostic, same place
11206    cx.update_editor(|editor, window, cx| {
11207        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11208    });
11209    cx.assert_editor_state(indoc! {"
11210        fn func(abc ˇdef: i32) -> u32 {
11211        }
11212    "});
11213
11214    // First diagnostic
11215    cx.update_editor(|editor, window, cx| {
11216        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11217    });
11218    cx.assert_editor_state(indoc! {"
11219        fn func(abcˇ def: i32) -> u32 {
11220        }
11221    "});
11222
11223    // Wrapped over, fourth diagnostic
11224    cx.update_editor(|editor, window, cx| {
11225        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11226    });
11227    cx.assert_editor_state(indoc! {"
11228        fn func(abc def: i32) -> ˇu32 {
11229        }
11230    "});
11231
11232    cx.update_editor(|editor, window, cx| {
11233        editor.move_to_beginning(&MoveToBeginning, window, cx);
11234    });
11235    cx.assert_editor_state(indoc! {"
11236        ˇfn func(abc def: i32) -> u32 {
11237        }
11238    "});
11239
11240    //// Forward
11241
11242    // First diagnostic
11243    cx.update_editor(|editor, window, cx| {
11244        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11245    });
11246    cx.assert_editor_state(indoc! {"
11247        fn func(abcˇ def: i32) -> u32 {
11248        }
11249    "});
11250
11251    // Second diagnostic
11252    cx.update_editor(|editor, window, cx| {
11253        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11254    });
11255    cx.assert_editor_state(indoc! {"
11256        fn func(abc ˇdef: i32) -> u32 {
11257        }
11258    "});
11259
11260    // Third diagnostic, same place
11261    cx.update_editor(|editor, window, cx| {
11262        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11263    });
11264    cx.assert_editor_state(indoc! {"
11265        fn func(abc ˇdef: i32) -> u32 {
11266        }
11267    "});
11268
11269    // Fourth diagnostic
11270    cx.update_editor(|editor, window, cx| {
11271        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11272    });
11273    cx.assert_editor_state(indoc! {"
11274        fn func(abc def: i32) -> ˇu32 {
11275        }
11276    "});
11277
11278    // Wrapped around, first diagnostic
11279    cx.update_editor(|editor, window, cx| {
11280        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11281    });
11282    cx.assert_editor_state(indoc! {"
11283        fn func(abcˇ def: i32) -> u32 {
11284        }
11285    "});
11286}
11287
11288#[gpui::test]
11289async fn active_diagnostics_dismiss_after_invalidation(
11290    executor: BackgroundExecutor,
11291    cx: &mut TestAppContext,
11292) {
11293    init_test(cx, |_| {});
11294
11295    let mut cx = EditorTestContext::new(cx).await;
11296    let lsp_store =
11297        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11298
11299    cx.set_state(indoc! {"
11300        ˇfn func(abc def: i32) -> u32 {
11301        }
11302    "});
11303
11304    let message = "Something's wrong!";
11305    cx.update(|_, cx| {
11306        lsp_store.update(cx, |lsp_store, cx| {
11307            lsp_store
11308                .update_diagnostics(
11309                    LanguageServerId(0),
11310                    lsp::PublishDiagnosticsParams {
11311                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11312                        version: None,
11313                        diagnostics: vec![lsp::Diagnostic {
11314                            range: lsp::Range::new(
11315                                lsp::Position::new(0, 11),
11316                                lsp::Position::new(0, 12),
11317                            ),
11318                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11319                            message: message.to_string(),
11320                            ..Default::default()
11321                        }],
11322                    },
11323                    &[],
11324                    cx,
11325                )
11326                .unwrap()
11327        });
11328    });
11329    executor.run_until_parked();
11330
11331    cx.update_editor(|editor, window, cx| {
11332        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11333        assert_eq!(
11334            editor
11335                .active_diagnostics
11336                .as_ref()
11337                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11338            Some(message),
11339            "Should have a diagnostics group activated"
11340        );
11341    });
11342    cx.assert_editor_state(indoc! {"
11343        fn func(abcˇ def: i32) -> u32 {
11344        }
11345    "});
11346
11347    cx.update(|_, cx| {
11348        lsp_store.update(cx, |lsp_store, cx| {
11349            lsp_store
11350                .update_diagnostics(
11351                    LanguageServerId(0),
11352                    lsp::PublishDiagnosticsParams {
11353                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11354                        version: None,
11355                        diagnostics: Vec::new(),
11356                    },
11357                    &[],
11358                    cx,
11359                )
11360                .unwrap()
11361        });
11362    });
11363    executor.run_until_parked();
11364    cx.update_editor(|editor, _, _| {
11365        assert_eq!(
11366            editor.active_diagnostics, None,
11367            "After no diagnostics set to the editor, no diagnostics should be active"
11368        );
11369    });
11370    cx.assert_editor_state(indoc! {"
11371        fn func(abcˇ def: i32) -> u32 {
11372        }
11373    "});
11374
11375    cx.update_editor(|editor, window, cx| {
11376        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11377        assert_eq!(
11378            editor.active_diagnostics, None,
11379            "Should be no diagnostics to go to and activate"
11380        );
11381    });
11382    cx.assert_editor_state(indoc! {"
11383        fn func(abcˇ def: i32) -> u32 {
11384        }
11385    "});
11386}
11387
11388#[gpui::test]
11389async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11390    init_test(cx, |_| {});
11391
11392    let mut cx = EditorTestContext::new(cx).await;
11393
11394    cx.set_state(indoc! {"
11395        fn func(abˇc def: i32) -> u32 {
11396        }
11397    "});
11398    let lsp_store =
11399        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11400
11401    cx.update(|_, cx| {
11402        lsp_store.update(cx, |lsp_store, cx| {
11403            lsp_store.update_diagnostics(
11404                LanguageServerId(0),
11405                lsp::PublishDiagnosticsParams {
11406                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11407                    version: None,
11408                    diagnostics: vec![lsp::Diagnostic {
11409                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11410                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11411                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11412                        ..Default::default()
11413                    }],
11414                },
11415                &[],
11416                cx,
11417            )
11418        })
11419    }).unwrap();
11420    cx.run_until_parked();
11421    cx.update_editor(|editor, window, cx| {
11422        hover_popover::hover(editor, &Default::default(), window, cx)
11423    });
11424    cx.run_until_parked();
11425    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11426}
11427
11428#[gpui::test]
11429async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11430    init_test(cx, |_| {});
11431
11432    let mut cx = EditorTestContext::new(cx).await;
11433
11434    let diff_base = r#"
11435        use some::mod;
11436
11437        const A: u32 = 42;
11438
11439        fn main() {
11440            println!("hello");
11441
11442            println!("world");
11443        }
11444        "#
11445    .unindent();
11446
11447    // Edits are modified, removed, modified, added
11448    cx.set_state(
11449        &r#"
11450        use some::modified;
11451
11452        ˇ
11453        fn main() {
11454            println!("hello there");
11455
11456            println!("around the");
11457            println!("world");
11458        }
11459        "#
11460        .unindent(),
11461    );
11462
11463    cx.set_head_text(&diff_base);
11464    executor.run_until_parked();
11465
11466    cx.update_editor(|editor, window, cx| {
11467        //Wrap around the bottom of the buffer
11468        for _ in 0..3 {
11469            editor.go_to_next_hunk(&GoToHunk, window, cx);
11470        }
11471    });
11472
11473    cx.assert_editor_state(
11474        &r#"
11475        ˇuse some::modified;
11476
11477
11478        fn main() {
11479            println!("hello there");
11480
11481            println!("around the");
11482            println!("world");
11483        }
11484        "#
11485        .unindent(),
11486    );
11487
11488    cx.update_editor(|editor, window, cx| {
11489        //Wrap around the top of the buffer
11490        for _ in 0..2 {
11491            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11492        }
11493    });
11494
11495    cx.assert_editor_state(
11496        &r#"
11497        use some::modified;
11498
11499
11500        fn main() {
11501        ˇ    println!("hello there");
11502
11503            println!("around the");
11504            println!("world");
11505        }
11506        "#
11507        .unindent(),
11508    );
11509
11510    cx.update_editor(|editor, window, cx| {
11511        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11512    });
11513
11514    cx.assert_editor_state(
11515        &r#"
11516        use some::modified;
11517
11518        ˇ
11519        fn main() {
11520            println!("hello there");
11521
11522            println!("around the");
11523            println!("world");
11524        }
11525        "#
11526        .unindent(),
11527    );
11528
11529    cx.update_editor(|editor, window, cx| {
11530        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11531    });
11532
11533    cx.assert_editor_state(
11534        &r#"
11535        ˇuse some::modified;
11536
11537
11538        fn main() {
11539            println!("hello there");
11540
11541            println!("around the");
11542            println!("world");
11543        }
11544        "#
11545        .unindent(),
11546    );
11547
11548    cx.update_editor(|editor, window, cx| {
11549        for _ in 0..2 {
11550            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11551        }
11552    });
11553
11554    cx.assert_editor_state(
11555        &r#"
11556        use some::modified;
11557
11558
11559        fn main() {
11560        ˇ    println!("hello there");
11561
11562            println!("around the");
11563            println!("world");
11564        }
11565        "#
11566        .unindent(),
11567    );
11568
11569    cx.update_editor(|editor, window, cx| {
11570        editor.fold(&Fold, window, cx);
11571    });
11572
11573    cx.update_editor(|editor, window, cx| {
11574        editor.go_to_next_hunk(&GoToHunk, window, cx);
11575    });
11576
11577    cx.assert_editor_state(
11578        &r#"
11579        ˇuse some::modified;
11580
11581
11582        fn main() {
11583            println!("hello there");
11584
11585            println!("around the");
11586            println!("world");
11587        }
11588        "#
11589        .unindent(),
11590    );
11591}
11592
11593#[test]
11594fn test_split_words() {
11595    fn split(text: &str) -> Vec<&str> {
11596        split_words(text).collect()
11597    }
11598
11599    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11600    assert_eq!(split("hello_world"), &["hello_", "world"]);
11601    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11602    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11603    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11604    assert_eq!(split("helloworld"), &["helloworld"]);
11605
11606    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11607}
11608
11609#[gpui::test]
11610async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11611    init_test(cx, |_| {});
11612
11613    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11614    let mut assert = |before, after| {
11615        let _state_context = cx.set_state(before);
11616        cx.run_until_parked();
11617        cx.update_editor(|editor, window, cx| {
11618            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11619        });
11620        cx.assert_editor_state(after);
11621    };
11622
11623    // Outside bracket jumps to outside of matching bracket
11624    assert("console.logˇ(var);", "console.log(var)ˇ;");
11625    assert("console.log(var)ˇ;", "console.logˇ(var);");
11626
11627    // Inside bracket jumps to inside of matching bracket
11628    assert("console.log(ˇvar);", "console.log(varˇ);");
11629    assert("console.log(varˇ);", "console.log(ˇvar);");
11630
11631    // When outside a bracket and inside, favor jumping to the inside bracket
11632    assert(
11633        "console.log('foo', [1, 2, 3]ˇ);",
11634        "console.log(ˇ'foo', [1, 2, 3]);",
11635    );
11636    assert(
11637        "console.log(ˇ'foo', [1, 2, 3]);",
11638        "console.log('foo', [1, 2, 3]ˇ);",
11639    );
11640
11641    // Bias forward if two options are equally likely
11642    assert(
11643        "let result = curried_fun()ˇ();",
11644        "let result = curried_fun()()ˇ;",
11645    );
11646
11647    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11648    assert(
11649        indoc! {"
11650            function test() {
11651                console.log('test')ˇ
11652            }"},
11653        indoc! {"
11654            function test() {
11655                console.logˇ('test')
11656            }"},
11657    );
11658}
11659
11660#[gpui::test]
11661async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11662    init_test(cx, |_| {});
11663
11664    let fs = FakeFs::new(cx.executor());
11665    fs.insert_tree(
11666        path!("/a"),
11667        json!({
11668            "main.rs": "fn main() { let a = 5; }",
11669            "other.rs": "// Test file",
11670        }),
11671    )
11672    .await;
11673    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11674
11675    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11676    language_registry.add(Arc::new(Language::new(
11677        LanguageConfig {
11678            name: "Rust".into(),
11679            matcher: LanguageMatcher {
11680                path_suffixes: vec!["rs".to_string()],
11681                ..Default::default()
11682            },
11683            brackets: BracketPairConfig {
11684                pairs: vec![BracketPair {
11685                    start: "{".to_string(),
11686                    end: "}".to_string(),
11687                    close: true,
11688                    surround: true,
11689                    newline: true,
11690                }],
11691                disabled_scopes_by_bracket_ix: Vec::new(),
11692            },
11693            ..Default::default()
11694        },
11695        Some(tree_sitter_rust::LANGUAGE.into()),
11696    )));
11697    let mut fake_servers = language_registry.register_fake_lsp(
11698        "Rust",
11699        FakeLspAdapter {
11700            capabilities: lsp::ServerCapabilities {
11701                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11702                    first_trigger_character: "{".to_string(),
11703                    more_trigger_character: None,
11704                }),
11705                ..Default::default()
11706            },
11707            ..Default::default()
11708        },
11709    );
11710
11711    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11712
11713    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11714
11715    let worktree_id = workspace
11716        .update(cx, |workspace, _, cx| {
11717            workspace.project().update(cx, |project, cx| {
11718                project.worktrees(cx).next().unwrap().read(cx).id()
11719            })
11720        })
11721        .unwrap();
11722
11723    let buffer = project
11724        .update(cx, |project, cx| {
11725            project.open_local_buffer(path!("/a/main.rs"), cx)
11726        })
11727        .await
11728        .unwrap();
11729    let editor_handle = workspace
11730        .update(cx, |workspace, window, cx| {
11731            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11732        })
11733        .unwrap()
11734        .await
11735        .unwrap()
11736        .downcast::<Editor>()
11737        .unwrap();
11738
11739    cx.executor().start_waiting();
11740    let fake_server = fake_servers.next().await.unwrap();
11741
11742    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11743        assert_eq!(
11744            params.text_document_position.text_document.uri,
11745            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11746        );
11747        assert_eq!(
11748            params.text_document_position.position,
11749            lsp::Position::new(0, 21),
11750        );
11751
11752        Ok(Some(vec![lsp::TextEdit {
11753            new_text: "]".to_string(),
11754            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11755        }]))
11756    });
11757
11758    editor_handle.update_in(cx, |editor, window, cx| {
11759        window.focus(&editor.focus_handle(cx));
11760        editor.change_selections(None, window, cx, |s| {
11761            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11762        });
11763        editor.handle_input("{", window, cx);
11764    });
11765
11766    cx.executor().run_until_parked();
11767
11768    buffer.update(cx, |buffer, _| {
11769        assert_eq!(
11770            buffer.text(),
11771            "fn main() { let a = {5}; }",
11772            "No extra braces from on type formatting should appear in the buffer"
11773        )
11774    });
11775}
11776
11777#[gpui::test]
11778async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11779    init_test(cx, |_| {});
11780
11781    let fs = FakeFs::new(cx.executor());
11782    fs.insert_tree(
11783        path!("/a"),
11784        json!({
11785            "main.rs": "fn main() { let a = 5; }",
11786            "other.rs": "// Test file",
11787        }),
11788    )
11789    .await;
11790
11791    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11792
11793    let server_restarts = Arc::new(AtomicUsize::new(0));
11794    let closure_restarts = Arc::clone(&server_restarts);
11795    let language_server_name = "test language server";
11796    let language_name: LanguageName = "Rust".into();
11797
11798    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11799    language_registry.add(Arc::new(Language::new(
11800        LanguageConfig {
11801            name: language_name.clone(),
11802            matcher: LanguageMatcher {
11803                path_suffixes: vec!["rs".to_string()],
11804                ..Default::default()
11805            },
11806            ..Default::default()
11807        },
11808        Some(tree_sitter_rust::LANGUAGE.into()),
11809    )));
11810    let mut fake_servers = language_registry.register_fake_lsp(
11811        "Rust",
11812        FakeLspAdapter {
11813            name: language_server_name,
11814            initialization_options: Some(json!({
11815                "testOptionValue": true
11816            })),
11817            initializer: Some(Box::new(move |fake_server| {
11818                let task_restarts = Arc::clone(&closure_restarts);
11819                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11820                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11821                    futures::future::ready(Ok(()))
11822                });
11823            })),
11824            ..Default::default()
11825        },
11826    );
11827
11828    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11829    let _buffer = project
11830        .update(cx, |project, cx| {
11831            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11832        })
11833        .await
11834        .unwrap();
11835    let _fake_server = fake_servers.next().await.unwrap();
11836    update_test_language_settings(cx, |language_settings| {
11837        language_settings.languages.insert(
11838            language_name.clone(),
11839            LanguageSettingsContent {
11840                tab_size: NonZeroU32::new(8),
11841                ..Default::default()
11842            },
11843        );
11844    });
11845    cx.executor().run_until_parked();
11846    assert_eq!(
11847        server_restarts.load(atomic::Ordering::Acquire),
11848        0,
11849        "Should not restart LSP server on an unrelated change"
11850    );
11851
11852    update_test_project_settings(cx, |project_settings| {
11853        project_settings.lsp.insert(
11854            "Some other server name".into(),
11855            LspSettings {
11856                binary: None,
11857                settings: None,
11858                initialization_options: Some(json!({
11859                    "some other init value": false
11860                })),
11861            },
11862        );
11863    });
11864    cx.executor().run_until_parked();
11865    assert_eq!(
11866        server_restarts.load(atomic::Ordering::Acquire),
11867        0,
11868        "Should not restart LSP server on an unrelated LSP settings change"
11869    );
11870
11871    update_test_project_settings(cx, |project_settings| {
11872        project_settings.lsp.insert(
11873            language_server_name.into(),
11874            LspSettings {
11875                binary: None,
11876                settings: None,
11877                initialization_options: Some(json!({
11878                    "anotherInitValue": false
11879                })),
11880            },
11881        );
11882    });
11883    cx.executor().run_until_parked();
11884    assert_eq!(
11885        server_restarts.load(atomic::Ordering::Acquire),
11886        1,
11887        "Should restart LSP server on a related LSP settings change"
11888    );
11889
11890    update_test_project_settings(cx, |project_settings| {
11891        project_settings.lsp.insert(
11892            language_server_name.into(),
11893            LspSettings {
11894                binary: None,
11895                settings: None,
11896                initialization_options: Some(json!({
11897                    "anotherInitValue": false
11898                })),
11899            },
11900        );
11901    });
11902    cx.executor().run_until_parked();
11903    assert_eq!(
11904        server_restarts.load(atomic::Ordering::Acquire),
11905        1,
11906        "Should not restart LSP server on a related LSP settings change that is the same"
11907    );
11908
11909    update_test_project_settings(cx, |project_settings| {
11910        project_settings.lsp.insert(
11911            language_server_name.into(),
11912            LspSettings {
11913                binary: None,
11914                settings: None,
11915                initialization_options: None,
11916            },
11917        );
11918    });
11919    cx.executor().run_until_parked();
11920    assert_eq!(
11921        server_restarts.load(atomic::Ordering::Acquire),
11922        2,
11923        "Should restart LSP server on another related LSP settings change"
11924    );
11925}
11926
11927#[gpui::test]
11928async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11929    init_test(cx, |_| {});
11930
11931    let mut cx = EditorLspTestContext::new_rust(
11932        lsp::ServerCapabilities {
11933            completion_provider: Some(lsp::CompletionOptions {
11934                trigger_characters: Some(vec![".".to_string()]),
11935                resolve_provider: Some(true),
11936                ..Default::default()
11937            }),
11938            ..Default::default()
11939        },
11940        cx,
11941    )
11942    .await;
11943
11944    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11945    cx.simulate_keystroke(".");
11946    let completion_item = lsp::CompletionItem {
11947        label: "some".into(),
11948        kind: Some(lsp::CompletionItemKind::SNIPPET),
11949        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11950        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11951            kind: lsp::MarkupKind::Markdown,
11952            value: "```rust\nSome(2)\n```".to_string(),
11953        })),
11954        deprecated: Some(false),
11955        sort_text: Some("fffffff2".to_string()),
11956        filter_text: Some("some".to_string()),
11957        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11958        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11959            range: lsp::Range {
11960                start: lsp::Position {
11961                    line: 0,
11962                    character: 22,
11963                },
11964                end: lsp::Position {
11965                    line: 0,
11966                    character: 22,
11967                },
11968            },
11969            new_text: "Some(2)".to_string(),
11970        })),
11971        additional_text_edits: Some(vec![lsp::TextEdit {
11972            range: lsp::Range {
11973                start: lsp::Position {
11974                    line: 0,
11975                    character: 20,
11976                },
11977                end: lsp::Position {
11978                    line: 0,
11979                    character: 22,
11980                },
11981            },
11982            new_text: "".to_string(),
11983        }]),
11984        ..Default::default()
11985    };
11986
11987    let closure_completion_item = completion_item.clone();
11988    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11989        let task_completion_item = closure_completion_item.clone();
11990        async move {
11991            Ok(Some(lsp::CompletionResponse::Array(vec![
11992                task_completion_item,
11993            ])))
11994        }
11995    });
11996
11997    request.next().await;
11998
11999    cx.condition(|editor, _| editor.context_menu_visible())
12000        .await;
12001    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12002        editor
12003            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12004            .unwrap()
12005    });
12006    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12007
12008    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12009        let task_completion_item = completion_item.clone();
12010        async move { Ok(task_completion_item) }
12011    })
12012    .next()
12013    .await
12014    .unwrap();
12015    apply_additional_edits.await.unwrap();
12016    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12017}
12018
12019#[gpui::test]
12020async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12021    init_test(cx, |_| {});
12022
12023    let mut cx = EditorLspTestContext::new_rust(
12024        lsp::ServerCapabilities {
12025            completion_provider: Some(lsp::CompletionOptions {
12026                trigger_characters: Some(vec![".".to_string()]),
12027                resolve_provider: Some(true),
12028                ..Default::default()
12029            }),
12030            ..Default::default()
12031        },
12032        cx,
12033    )
12034    .await;
12035
12036    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12037    cx.simulate_keystroke(".");
12038
12039    let item1 = lsp::CompletionItem {
12040        label: "method id()".to_string(),
12041        filter_text: Some("id".to_string()),
12042        detail: None,
12043        documentation: None,
12044        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12045            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12046            new_text: ".id".to_string(),
12047        })),
12048        ..lsp::CompletionItem::default()
12049    };
12050
12051    let item2 = lsp::CompletionItem {
12052        label: "other".to_string(),
12053        filter_text: Some("other".to_string()),
12054        detail: None,
12055        documentation: None,
12056        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12057            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12058            new_text: ".other".to_string(),
12059        })),
12060        ..lsp::CompletionItem::default()
12061    };
12062
12063    let item1 = item1.clone();
12064    cx.handle_request::<lsp::request::Completion, _, _>({
12065        let item1 = item1.clone();
12066        move |_, _, _| {
12067            let item1 = item1.clone();
12068            let item2 = item2.clone();
12069            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12070        }
12071    })
12072    .next()
12073    .await;
12074
12075    cx.condition(|editor, _| editor.context_menu_visible())
12076        .await;
12077    cx.update_editor(|editor, _, _| {
12078        let context_menu = editor.context_menu.borrow_mut();
12079        let context_menu = context_menu
12080            .as_ref()
12081            .expect("Should have the context menu deployed");
12082        match context_menu {
12083            CodeContextMenu::Completions(completions_menu) => {
12084                let completions = completions_menu.completions.borrow_mut();
12085                assert_eq!(
12086                    completions
12087                        .iter()
12088                        .map(|completion| &completion.label.text)
12089                        .collect::<Vec<_>>(),
12090                    vec!["method id()", "other"]
12091                )
12092            }
12093            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12094        }
12095    });
12096
12097    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12098        let item1 = item1.clone();
12099        move |_, item_to_resolve, _| {
12100            let item1 = item1.clone();
12101            async move {
12102                if item1 == item_to_resolve {
12103                    Ok(lsp::CompletionItem {
12104                        label: "method id()".to_string(),
12105                        filter_text: Some("id".to_string()),
12106                        detail: Some("Now resolved!".to_string()),
12107                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12108                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12109                            range: lsp::Range::new(
12110                                lsp::Position::new(0, 22),
12111                                lsp::Position::new(0, 22),
12112                            ),
12113                            new_text: ".id".to_string(),
12114                        })),
12115                        ..lsp::CompletionItem::default()
12116                    })
12117                } else {
12118                    Ok(item_to_resolve)
12119                }
12120            }
12121        }
12122    })
12123    .next()
12124    .await
12125    .unwrap();
12126    cx.run_until_parked();
12127
12128    cx.update_editor(|editor, window, cx| {
12129        editor.context_menu_next(&Default::default(), window, cx);
12130    });
12131
12132    cx.update_editor(|editor, _, _| {
12133        let context_menu = editor.context_menu.borrow_mut();
12134        let context_menu = context_menu
12135            .as_ref()
12136            .expect("Should have the context menu deployed");
12137        match context_menu {
12138            CodeContextMenu::Completions(completions_menu) => {
12139                let completions = completions_menu.completions.borrow_mut();
12140                assert_eq!(
12141                    completions
12142                        .iter()
12143                        .map(|completion| &completion.label.text)
12144                        .collect::<Vec<_>>(),
12145                    vec!["method id() Now resolved!", "other"],
12146                    "Should update first completion label, but not second as the filter text did not match."
12147                );
12148            }
12149            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12150        }
12151    });
12152}
12153
12154#[gpui::test]
12155async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12156    init_test(cx, |_| {});
12157
12158    let mut cx = EditorLspTestContext::new_rust(
12159        lsp::ServerCapabilities {
12160            completion_provider: Some(lsp::CompletionOptions {
12161                trigger_characters: Some(vec![".".to_string()]),
12162                resolve_provider: Some(true),
12163                ..Default::default()
12164            }),
12165            ..Default::default()
12166        },
12167        cx,
12168    )
12169    .await;
12170
12171    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12172    cx.simulate_keystroke(".");
12173
12174    let unresolved_item_1 = lsp::CompletionItem {
12175        label: "id".to_string(),
12176        filter_text: Some("id".to_string()),
12177        detail: None,
12178        documentation: None,
12179        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12180            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12181            new_text: ".id".to_string(),
12182        })),
12183        ..lsp::CompletionItem::default()
12184    };
12185    let resolved_item_1 = lsp::CompletionItem {
12186        additional_text_edits: Some(vec![lsp::TextEdit {
12187            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12188            new_text: "!!".to_string(),
12189        }]),
12190        ..unresolved_item_1.clone()
12191    };
12192    let unresolved_item_2 = lsp::CompletionItem {
12193        label: "other".to_string(),
12194        filter_text: Some("other".to_string()),
12195        detail: None,
12196        documentation: None,
12197        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12198            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12199            new_text: ".other".to_string(),
12200        })),
12201        ..lsp::CompletionItem::default()
12202    };
12203    let resolved_item_2 = lsp::CompletionItem {
12204        additional_text_edits: Some(vec![lsp::TextEdit {
12205            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12206            new_text: "??".to_string(),
12207        }]),
12208        ..unresolved_item_2.clone()
12209    };
12210
12211    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12212    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12213    cx.lsp
12214        .server
12215        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12216            let unresolved_item_1 = unresolved_item_1.clone();
12217            let resolved_item_1 = resolved_item_1.clone();
12218            let unresolved_item_2 = unresolved_item_2.clone();
12219            let resolved_item_2 = resolved_item_2.clone();
12220            let resolve_requests_1 = resolve_requests_1.clone();
12221            let resolve_requests_2 = resolve_requests_2.clone();
12222            move |unresolved_request, _| {
12223                let unresolved_item_1 = unresolved_item_1.clone();
12224                let resolved_item_1 = resolved_item_1.clone();
12225                let unresolved_item_2 = unresolved_item_2.clone();
12226                let resolved_item_2 = resolved_item_2.clone();
12227                let resolve_requests_1 = resolve_requests_1.clone();
12228                let resolve_requests_2 = resolve_requests_2.clone();
12229                async move {
12230                    if unresolved_request == unresolved_item_1 {
12231                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12232                        Ok(resolved_item_1.clone())
12233                    } else if unresolved_request == unresolved_item_2 {
12234                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12235                        Ok(resolved_item_2.clone())
12236                    } else {
12237                        panic!("Unexpected completion item {unresolved_request:?}")
12238                    }
12239                }
12240            }
12241        })
12242        .detach();
12243
12244    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12245        let unresolved_item_1 = unresolved_item_1.clone();
12246        let unresolved_item_2 = unresolved_item_2.clone();
12247        async move {
12248            Ok(Some(lsp::CompletionResponse::Array(vec![
12249                unresolved_item_1,
12250                unresolved_item_2,
12251            ])))
12252        }
12253    })
12254    .next()
12255    .await;
12256
12257    cx.condition(|editor, _| editor.context_menu_visible())
12258        .await;
12259    cx.update_editor(|editor, _, _| {
12260        let context_menu = editor.context_menu.borrow_mut();
12261        let context_menu = context_menu
12262            .as_ref()
12263            .expect("Should have the context menu deployed");
12264        match context_menu {
12265            CodeContextMenu::Completions(completions_menu) => {
12266                let completions = completions_menu.completions.borrow_mut();
12267                assert_eq!(
12268                    completions
12269                        .iter()
12270                        .map(|completion| &completion.label.text)
12271                        .collect::<Vec<_>>(),
12272                    vec!["id", "other"]
12273                )
12274            }
12275            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12276        }
12277    });
12278    cx.run_until_parked();
12279
12280    cx.update_editor(|editor, window, cx| {
12281        editor.context_menu_next(&ContextMenuNext, window, cx);
12282    });
12283    cx.run_until_parked();
12284    cx.update_editor(|editor, window, cx| {
12285        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12286    });
12287    cx.run_until_parked();
12288    cx.update_editor(|editor, window, cx| {
12289        editor.context_menu_next(&ContextMenuNext, window, cx);
12290    });
12291    cx.run_until_parked();
12292    cx.update_editor(|editor, window, cx| {
12293        editor
12294            .compose_completion(&ComposeCompletion::default(), window, cx)
12295            .expect("No task returned")
12296    })
12297    .await
12298    .expect("Completion failed");
12299    cx.run_until_parked();
12300
12301    cx.update_editor(|editor, _, cx| {
12302        assert_eq!(
12303            resolve_requests_1.load(atomic::Ordering::Acquire),
12304            1,
12305            "Should always resolve once despite multiple selections"
12306        );
12307        assert_eq!(
12308            resolve_requests_2.load(atomic::Ordering::Acquire),
12309            1,
12310            "Should always resolve once after multiple selections and applying the completion"
12311        );
12312        assert_eq!(
12313            editor.text(cx),
12314            "fn main() { let a = ??.other; }",
12315            "Should use resolved data when applying the completion"
12316        );
12317    });
12318}
12319
12320#[gpui::test]
12321async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12322    init_test(cx, |_| {});
12323
12324    let item_0 = lsp::CompletionItem {
12325        label: "abs".into(),
12326        insert_text: Some("abs".into()),
12327        data: Some(json!({ "very": "special"})),
12328        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12329        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12330            lsp::InsertReplaceEdit {
12331                new_text: "abs".to_string(),
12332                insert: lsp::Range::default(),
12333                replace: lsp::Range::default(),
12334            },
12335        )),
12336        ..lsp::CompletionItem::default()
12337    };
12338    let items = iter::once(item_0.clone())
12339        .chain((11..51).map(|i| lsp::CompletionItem {
12340            label: format!("item_{}", i),
12341            insert_text: Some(format!("item_{}", i)),
12342            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12343            ..lsp::CompletionItem::default()
12344        }))
12345        .collect::<Vec<_>>();
12346
12347    let default_commit_characters = vec!["?".to_string()];
12348    let default_data = json!({ "default": "data"});
12349    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12350    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12351    let default_edit_range = lsp::Range {
12352        start: lsp::Position {
12353            line: 0,
12354            character: 5,
12355        },
12356        end: lsp::Position {
12357            line: 0,
12358            character: 5,
12359        },
12360    };
12361
12362    let item_0_out = lsp::CompletionItem {
12363        commit_characters: Some(default_commit_characters.clone()),
12364        insert_text_format: Some(default_insert_text_format),
12365        ..item_0
12366    };
12367    let items_out = iter::once(item_0_out)
12368        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
12369            commit_characters: Some(default_commit_characters.clone()),
12370            data: Some(default_data.clone()),
12371            insert_text_mode: Some(default_insert_text_mode),
12372            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12373                range: default_edit_range,
12374                new_text: item.label.clone(),
12375            })),
12376            ..item.clone()
12377        }))
12378        .collect::<Vec<lsp::CompletionItem>>();
12379
12380    let mut cx = EditorLspTestContext::new_rust(
12381        lsp::ServerCapabilities {
12382            completion_provider: Some(lsp::CompletionOptions {
12383                trigger_characters: Some(vec![".".to_string()]),
12384                resolve_provider: Some(true),
12385                ..Default::default()
12386            }),
12387            ..Default::default()
12388        },
12389        cx,
12390    )
12391    .await;
12392
12393    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12394    cx.simulate_keystroke(".");
12395
12396    let completion_data = default_data.clone();
12397    let completion_characters = default_commit_characters.clone();
12398    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12399        let default_data = completion_data.clone();
12400        let default_commit_characters = completion_characters.clone();
12401        let items = items.clone();
12402        async move {
12403            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12404                items,
12405                item_defaults: Some(lsp::CompletionListItemDefaults {
12406                    data: Some(default_data.clone()),
12407                    commit_characters: Some(default_commit_characters.clone()),
12408                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12409                        default_edit_range,
12410                    )),
12411                    insert_text_format: Some(default_insert_text_format),
12412                    insert_text_mode: Some(default_insert_text_mode),
12413                }),
12414                ..lsp::CompletionList::default()
12415            })))
12416        }
12417    })
12418    .next()
12419    .await;
12420
12421    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12422    cx.lsp
12423        .server
12424        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12425            let closure_resolved_items = resolved_items.clone();
12426            move |item_to_resolve, _| {
12427                let closure_resolved_items = closure_resolved_items.clone();
12428                async move {
12429                    closure_resolved_items.lock().push(item_to_resolve.clone());
12430                    Ok(item_to_resolve)
12431                }
12432            }
12433        })
12434        .detach();
12435
12436    cx.condition(|editor, _| editor.context_menu_visible())
12437        .await;
12438    cx.run_until_parked();
12439    cx.update_editor(|editor, _, _| {
12440        let menu = editor.context_menu.borrow_mut();
12441        match menu.as_ref().expect("should have the completions menu") {
12442            CodeContextMenu::Completions(completions_menu) => {
12443                assert_eq!(
12444                    completions_menu
12445                        .entries
12446                        .borrow()
12447                        .iter()
12448                        .map(|mat| mat.string.clone())
12449                        .collect::<Vec<String>>(),
12450                    items_out
12451                        .iter()
12452                        .map(|completion| completion.label.clone())
12453                        .collect::<Vec<String>>()
12454                );
12455            }
12456            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12457        }
12458    });
12459    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12460    // with 4 from the end.
12461    assert_eq!(
12462        *resolved_items.lock(),
12463        [
12464            &items_out[0..16],
12465            &items_out[items_out.len() - 4..items_out.len()]
12466        ]
12467        .concat()
12468        .iter()
12469        .cloned()
12470        .collect::<Vec<lsp::CompletionItem>>()
12471    );
12472    resolved_items.lock().clear();
12473
12474    cx.update_editor(|editor, window, cx| {
12475        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12476    });
12477    cx.run_until_parked();
12478    // Completions that have already been resolved are skipped.
12479    assert_eq!(
12480        *resolved_items.lock(),
12481        items_out[items_out.len() - 16..items_out.len() - 4]
12482            .iter()
12483            .cloned()
12484            .collect::<Vec<lsp::CompletionItem>>()
12485    );
12486    resolved_items.lock().clear();
12487}
12488
12489#[gpui::test]
12490async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12491    init_test(cx, |_| {});
12492
12493    let mut cx = EditorLspTestContext::new(
12494        Language::new(
12495            LanguageConfig {
12496                matcher: LanguageMatcher {
12497                    path_suffixes: vec!["jsx".into()],
12498                    ..Default::default()
12499                },
12500                overrides: [(
12501                    "element".into(),
12502                    LanguageConfigOverride {
12503                        word_characters: Override::Set(['-'].into_iter().collect()),
12504                        ..Default::default()
12505                    },
12506                )]
12507                .into_iter()
12508                .collect(),
12509                ..Default::default()
12510            },
12511            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12512        )
12513        .with_override_query("(jsx_self_closing_element) @element")
12514        .unwrap(),
12515        lsp::ServerCapabilities {
12516            completion_provider: Some(lsp::CompletionOptions {
12517                trigger_characters: Some(vec![":".to_string()]),
12518                ..Default::default()
12519            }),
12520            ..Default::default()
12521        },
12522        cx,
12523    )
12524    .await;
12525
12526    cx.lsp
12527        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12528            Ok(Some(lsp::CompletionResponse::Array(vec![
12529                lsp::CompletionItem {
12530                    label: "bg-blue".into(),
12531                    ..Default::default()
12532                },
12533                lsp::CompletionItem {
12534                    label: "bg-red".into(),
12535                    ..Default::default()
12536                },
12537                lsp::CompletionItem {
12538                    label: "bg-yellow".into(),
12539                    ..Default::default()
12540                },
12541            ])))
12542        });
12543
12544    cx.set_state(r#"<p class="bgˇ" />"#);
12545
12546    // Trigger completion when typing a dash, because the dash is an extra
12547    // word character in the 'element' scope, which contains the cursor.
12548    cx.simulate_keystroke("-");
12549    cx.executor().run_until_parked();
12550    cx.update_editor(|editor, _, _| {
12551        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12552        {
12553            assert_eq!(
12554                completion_menu_entries(&menu),
12555                &["bg-red", "bg-blue", "bg-yellow"]
12556            );
12557        } else {
12558            panic!("expected completion menu to be open");
12559        }
12560    });
12561
12562    cx.simulate_keystroke("l");
12563    cx.executor().run_until_parked();
12564    cx.update_editor(|editor, _, _| {
12565        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12566        {
12567            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12568        } else {
12569            panic!("expected completion menu to be open");
12570        }
12571    });
12572
12573    // When filtering completions, consider the character after the '-' to
12574    // be the start of a subword.
12575    cx.set_state(r#"<p class="yelˇ" />"#);
12576    cx.simulate_keystroke("l");
12577    cx.executor().run_until_parked();
12578    cx.update_editor(|editor, _, _| {
12579        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12580        {
12581            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12582        } else {
12583            panic!("expected completion menu to be open");
12584        }
12585    });
12586}
12587
12588fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12589    let entries = menu.entries.borrow();
12590    entries.iter().map(|mat| mat.string.clone()).collect()
12591}
12592
12593#[gpui::test]
12594async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12595    init_test(cx, |settings| {
12596        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12597            FormatterList(vec![Formatter::Prettier].into()),
12598        ))
12599    });
12600
12601    let fs = FakeFs::new(cx.executor());
12602    fs.insert_file(path!("/file.ts"), Default::default()).await;
12603
12604    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12605    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12606
12607    language_registry.add(Arc::new(Language::new(
12608        LanguageConfig {
12609            name: "TypeScript".into(),
12610            matcher: LanguageMatcher {
12611                path_suffixes: vec!["ts".to_string()],
12612                ..Default::default()
12613            },
12614            ..Default::default()
12615        },
12616        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12617    )));
12618    update_test_language_settings(cx, |settings| {
12619        settings.defaults.prettier = Some(PrettierSettings {
12620            allowed: true,
12621            ..PrettierSettings::default()
12622        });
12623    });
12624
12625    let test_plugin = "test_plugin";
12626    let _ = language_registry.register_fake_lsp(
12627        "TypeScript",
12628        FakeLspAdapter {
12629            prettier_plugins: vec![test_plugin],
12630            ..Default::default()
12631        },
12632    );
12633
12634    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12635    let buffer = project
12636        .update(cx, |project, cx| {
12637            project.open_local_buffer(path!("/file.ts"), cx)
12638        })
12639        .await
12640        .unwrap();
12641
12642    let buffer_text = "one\ntwo\nthree\n";
12643    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12644    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12645    editor.update_in(cx, |editor, window, cx| {
12646        editor.set_text(buffer_text, window, cx)
12647    });
12648
12649    editor
12650        .update_in(cx, |editor, window, cx| {
12651            editor.perform_format(
12652                project.clone(),
12653                FormatTrigger::Manual,
12654                FormatTarget::Buffers,
12655                window,
12656                cx,
12657            )
12658        })
12659        .unwrap()
12660        .await;
12661    assert_eq!(
12662        editor.update(cx, |editor, cx| editor.text(cx)),
12663        buffer_text.to_string() + prettier_format_suffix,
12664        "Test prettier formatting was not applied to the original buffer text",
12665    );
12666
12667    update_test_language_settings(cx, |settings| {
12668        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12669    });
12670    let format = editor.update_in(cx, |editor, window, cx| {
12671        editor.perform_format(
12672            project.clone(),
12673            FormatTrigger::Manual,
12674            FormatTarget::Buffers,
12675            window,
12676            cx,
12677        )
12678    });
12679    format.await.unwrap();
12680    assert_eq!(
12681        editor.update(cx, |editor, cx| editor.text(cx)),
12682        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12683        "Autoformatting (via test prettier) was not applied to the original buffer text",
12684    );
12685}
12686
12687#[gpui::test]
12688async fn test_addition_reverts(cx: &mut TestAppContext) {
12689    init_test(cx, |_| {});
12690    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12691    let base_text = indoc! {r#"
12692        struct Row;
12693        struct Row1;
12694        struct Row2;
12695
12696        struct Row4;
12697        struct Row5;
12698        struct Row6;
12699
12700        struct Row8;
12701        struct Row9;
12702        struct Row10;"#};
12703
12704    // When addition hunks are not adjacent to carets, no hunk revert is performed
12705    assert_hunk_revert(
12706        indoc! {r#"struct Row;
12707                   struct Row1;
12708                   struct Row1.1;
12709                   struct Row1.2;
12710                   struct Row2;ˇ
12711
12712                   struct Row4;
12713                   struct Row5;
12714                   struct Row6;
12715
12716                   struct Row8;
12717                   ˇstruct Row9;
12718                   struct Row9.1;
12719                   struct Row9.2;
12720                   struct Row9.3;
12721                   struct Row10;"#},
12722        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12723        indoc! {r#"struct Row;
12724                   struct Row1;
12725                   struct Row1.1;
12726                   struct Row1.2;
12727                   struct Row2;ˇ
12728
12729                   struct Row4;
12730                   struct Row5;
12731                   struct Row6;
12732
12733                   struct Row8;
12734                   ˇstruct Row9;
12735                   struct Row9.1;
12736                   struct Row9.2;
12737                   struct Row9.3;
12738                   struct Row10;"#},
12739        base_text,
12740        &mut cx,
12741    );
12742    // Same for selections
12743    assert_hunk_revert(
12744        indoc! {r#"struct Row;
12745                   struct Row1;
12746                   struct Row2;
12747                   struct Row2.1;
12748                   struct Row2.2;
12749                   «ˇ
12750                   struct Row4;
12751                   struct» Row5;
12752                   «struct Row6;
12753                   ˇ»
12754                   struct Row9.1;
12755                   struct Row9.2;
12756                   struct Row9.3;
12757                   struct Row8;
12758                   struct Row9;
12759                   struct Row10;"#},
12760        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12761        indoc! {r#"struct Row;
12762                   struct Row1;
12763                   struct Row2;
12764                   struct Row2.1;
12765                   struct Row2.2;
12766                   «ˇ
12767                   struct Row4;
12768                   struct» Row5;
12769                   «struct Row6;
12770                   ˇ»
12771                   struct Row9.1;
12772                   struct Row9.2;
12773                   struct Row9.3;
12774                   struct Row8;
12775                   struct Row9;
12776                   struct Row10;"#},
12777        base_text,
12778        &mut cx,
12779    );
12780
12781    // When carets and selections intersect the addition hunks, those are reverted.
12782    // Adjacent carets got merged.
12783    assert_hunk_revert(
12784        indoc! {r#"struct Row;
12785                   ˇ// something on the top
12786                   struct Row1;
12787                   struct Row2;
12788                   struct Roˇw3.1;
12789                   struct Row2.2;
12790                   struct Row2.3;ˇ
12791
12792                   struct Row4;
12793                   struct ˇRow5.1;
12794                   struct Row5.2;
12795                   struct «Rowˇ»5.3;
12796                   struct Row5;
12797                   struct Row6;
12798                   ˇ
12799                   struct Row9.1;
12800                   struct «Rowˇ»9.2;
12801                   struct «ˇRow»9.3;
12802                   struct Row8;
12803                   struct Row9;
12804                   «ˇ// something on bottom»
12805                   struct Row10;"#},
12806        vec![
12807            DiffHunkStatusKind::Added,
12808            DiffHunkStatusKind::Added,
12809            DiffHunkStatusKind::Added,
12810            DiffHunkStatusKind::Added,
12811            DiffHunkStatusKind::Added,
12812        ],
12813        indoc! {r#"struct Row;
12814                   ˇstruct Row1;
12815                   struct Row2;
12816                   ˇ
12817                   struct Row4;
12818                   ˇstruct Row5;
12819                   struct Row6;
12820                   ˇ
12821                   ˇstruct Row8;
12822                   struct Row9;
12823                   ˇstruct Row10;"#},
12824        base_text,
12825        &mut cx,
12826    );
12827}
12828
12829#[gpui::test]
12830async fn test_modification_reverts(cx: &mut TestAppContext) {
12831    init_test(cx, |_| {});
12832    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12833    let base_text = indoc! {r#"
12834        struct Row;
12835        struct Row1;
12836        struct Row2;
12837
12838        struct Row4;
12839        struct Row5;
12840        struct Row6;
12841
12842        struct Row8;
12843        struct Row9;
12844        struct Row10;"#};
12845
12846    // Modification hunks behave the same as the addition ones.
12847    assert_hunk_revert(
12848        indoc! {r#"struct Row;
12849                   struct Row1;
12850                   struct Row33;
12851                   ˇ
12852                   struct Row4;
12853                   struct Row5;
12854                   struct Row6;
12855                   ˇ
12856                   struct Row99;
12857                   struct Row9;
12858                   struct Row10;"#},
12859        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12860        indoc! {r#"struct Row;
12861                   struct Row1;
12862                   struct Row33;
12863                   ˇ
12864                   struct Row4;
12865                   struct Row5;
12866                   struct Row6;
12867                   ˇ
12868                   struct Row99;
12869                   struct Row9;
12870                   struct Row10;"#},
12871        base_text,
12872        &mut cx,
12873    );
12874    assert_hunk_revert(
12875        indoc! {r#"struct Row;
12876                   struct Row1;
12877                   struct Row33;
12878                   «ˇ
12879                   struct Row4;
12880                   struct» Row5;
12881                   «struct Row6;
12882                   ˇ»
12883                   struct Row99;
12884                   struct Row9;
12885                   struct Row10;"#},
12886        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12887        indoc! {r#"struct Row;
12888                   struct Row1;
12889                   struct Row33;
12890                   «ˇ
12891                   struct Row4;
12892                   struct» Row5;
12893                   «struct Row6;
12894                   ˇ»
12895                   struct Row99;
12896                   struct Row9;
12897                   struct Row10;"#},
12898        base_text,
12899        &mut cx,
12900    );
12901
12902    assert_hunk_revert(
12903        indoc! {r#"ˇstruct Row1.1;
12904                   struct Row1;
12905                   «ˇstr»uct Row22;
12906
12907                   struct ˇRow44;
12908                   struct Row5;
12909                   struct «Rˇ»ow66;ˇ
12910
12911                   «struˇ»ct Row88;
12912                   struct Row9;
12913                   struct Row1011;ˇ"#},
12914        vec![
12915            DiffHunkStatusKind::Modified,
12916            DiffHunkStatusKind::Modified,
12917            DiffHunkStatusKind::Modified,
12918            DiffHunkStatusKind::Modified,
12919            DiffHunkStatusKind::Modified,
12920            DiffHunkStatusKind::Modified,
12921        ],
12922        indoc! {r#"struct Row;
12923                   ˇstruct Row1;
12924                   struct Row2;
12925                   ˇ
12926                   struct Row4;
12927                   ˇstruct Row5;
12928                   struct Row6;
12929                   ˇ
12930                   struct Row8;
12931                   ˇstruct Row9;
12932                   struct Row10;ˇ"#},
12933        base_text,
12934        &mut cx,
12935    );
12936}
12937
12938#[gpui::test]
12939async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12940    init_test(cx, |_| {});
12941    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12942    let base_text = indoc! {r#"
12943        one
12944
12945        two
12946        three
12947        "#};
12948
12949    cx.set_head_text(base_text);
12950    cx.set_state("\nˇ\n");
12951    cx.executor().run_until_parked();
12952    cx.update_editor(|editor, _window, cx| {
12953        editor.expand_selected_diff_hunks(cx);
12954    });
12955    cx.executor().run_until_parked();
12956    cx.update_editor(|editor, window, cx| {
12957        editor.backspace(&Default::default(), window, cx);
12958    });
12959    cx.run_until_parked();
12960    cx.assert_state_with_diff(
12961        indoc! {r#"
12962
12963        - two
12964        - threeˇ
12965        +
12966        "#}
12967        .to_string(),
12968    );
12969}
12970
12971#[gpui::test]
12972async fn test_deletion_reverts(cx: &mut TestAppContext) {
12973    init_test(cx, |_| {});
12974    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12975    let base_text = indoc! {r#"struct Row;
12976struct Row1;
12977struct Row2;
12978
12979struct Row4;
12980struct Row5;
12981struct Row6;
12982
12983struct Row8;
12984struct Row9;
12985struct Row10;"#};
12986
12987    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12988    assert_hunk_revert(
12989        indoc! {r#"struct Row;
12990                   struct Row2;
12991
12992                   ˇstruct Row4;
12993                   struct Row5;
12994                   struct Row6;
12995                   ˇ
12996                   struct Row8;
12997                   struct Row10;"#},
12998        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12999        indoc! {r#"struct Row;
13000                   struct Row2;
13001
13002                   ˇstruct Row4;
13003                   struct Row5;
13004                   struct Row6;
13005                   ˇ
13006                   struct Row8;
13007                   struct Row10;"#},
13008        base_text,
13009        &mut cx,
13010    );
13011    assert_hunk_revert(
13012        indoc! {r#"struct Row;
13013                   struct Row2;
13014
13015                   «ˇstruct Row4;
13016                   struct» Row5;
13017                   «struct Row6;
13018                   ˇ»
13019                   struct Row8;
13020                   struct Row10;"#},
13021        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13022        indoc! {r#"struct Row;
13023                   struct Row2;
13024
13025                   «ˇstruct Row4;
13026                   struct» Row5;
13027                   «struct Row6;
13028                   ˇ»
13029                   struct Row8;
13030                   struct Row10;"#},
13031        base_text,
13032        &mut cx,
13033    );
13034
13035    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13036    assert_hunk_revert(
13037        indoc! {r#"struct Row;
13038                   ˇstruct Row2;
13039
13040                   struct Row4;
13041                   struct Row5;
13042                   struct Row6;
13043
13044                   struct Row8;ˇ
13045                   struct Row10;"#},
13046        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13047        indoc! {r#"struct Row;
13048                   struct Row1;
13049                   ˇstruct Row2;
13050
13051                   struct Row4;
13052                   struct Row5;
13053                   struct Row6;
13054
13055                   struct Row8;ˇ
13056                   struct Row9;
13057                   struct Row10;"#},
13058        base_text,
13059        &mut cx,
13060    );
13061    assert_hunk_revert(
13062        indoc! {r#"struct Row;
13063                   struct Row2«ˇ;
13064                   struct Row4;
13065                   struct» Row5;
13066                   «struct Row6;
13067
13068                   struct Row8;ˇ»
13069                   struct Row10;"#},
13070        vec![
13071            DiffHunkStatusKind::Deleted,
13072            DiffHunkStatusKind::Deleted,
13073            DiffHunkStatusKind::Deleted,
13074        ],
13075        indoc! {r#"struct Row;
13076                   struct Row1;
13077                   struct Row2«ˇ;
13078
13079                   struct Row4;
13080                   struct» Row5;
13081                   «struct Row6;
13082
13083                   struct Row8;ˇ»
13084                   struct Row9;
13085                   struct Row10;"#},
13086        base_text,
13087        &mut cx,
13088    );
13089}
13090
13091#[gpui::test]
13092async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13093    init_test(cx, |_| {});
13094
13095    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13096    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13097    let base_text_3 =
13098        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13099
13100    let text_1 = edit_first_char_of_every_line(base_text_1);
13101    let text_2 = edit_first_char_of_every_line(base_text_2);
13102    let text_3 = edit_first_char_of_every_line(base_text_3);
13103
13104    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13105    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13106    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13107
13108    let multibuffer = cx.new(|cx| {
13109        let mut multibuffer = MultiBuffer::new(ReadWrite);
13110        multibuffer.push_excerpts(
13111            buffer_1.clone(),
13112            [
13113                ExcerptRange {
13114                    context: Point::new(0, 0)..Point::new(3, 0),
13115                    primary: None,
13116                },
13117                ExcerptRange {
13118                    context: Point::new(5, 0)..Point::new(7, 0),
13119                    primary: None,
13120                },
13121                ExcerptRange {
13122                    context: Point::new(9, 0)..Point::new(10, 4),
13123                    primary: None,
13124                },
13125            ],
13126            cx,
13127        );
13128        multibuffer.push_excerpts(
13129            buffer_2.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_3.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
13165    });
13166
13167    let fs = FakeFs::new(cx.executor());
13168    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13169    let (editor, cx) = cx
13170        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13171    editor.update_in(cx, |editor, _window, cx| {
13172        for (buffer, diff_base) in [
13173            (buffer_1.clone(), base_text_1),
13174            (buffer_2.clone(), base_text_2),
13175            (buffer_3.clone(), base_text_3),
13176        ] {
13177            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13178            editor
13179                .buffer
13180                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13181        }
13182    });
13183    cx.executor().run_until_parked();
13184
13185    editor.update_in(cx, |editor, window, cx| {
13186        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}");
13187        editor.select_all(&SelectAll, window, cx);
13188        editor.git_restore(&Default::default(), window, cx);
13189    });
13190    cx.executor().run_until_parked();
13191
13192    // When all ranges are selected, all buffer hunks are reverted.
13193    editor.update(cx, |editor, cx| {
13194        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");
13195    });
13196    buffer_1.update(cx, |buffer, _| {
13197        assert_eq!(buffer.text(), base_text_1);
13198    });
13199    buffer_2.update(cx, |buffer, _| {
13200        assert_eq!(buffer.text(), base_text_2);
13201    });
13202    buffer_3.update(cx, |buffer, _| {
13203        assert_eq!(buffer.text(), base_text_3);
13204    });
13205
13206    editor.update_in(cx, |editor, window, cx| {
13207        editor.undo(&Default::default(), window, cx);
13208    });
13209
13210    editor.update_in(cx, |editor, window, cx| {
13211        editor.change_selections(None, window, cx, |s| {
13212            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13213        });
13214        editor.git_restore(&Default::default(), window, cx);
13215    });
13216
13217    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13218    // but not affect buffer_2 and its related excerpts.
13219    editor.update(cx, |editor, cx| {
13220        assert_eq!(
13221            editor.text(cx),
13222            "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}"
13223        );
13224    });
13225    buffer_1.update(cx, |buffer, _| {
13226        assert_eq!(buffer.text(), base_text_1);
13227    });
13228    buffer_2.update(cx, |buffer, _| {
13229        assert_eq!(
13230            buffer.text(),
13231            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13232        );
13233    });
13234    buffer_3.update(cx, |buffer, _| {
13235        assert_eq!(
13236            buffer.text(),
13237            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13238        );
13239    });
13240
13241    fn edit_first_char_of_every_line(text: &str) -> String {
13242        text.split('\n')
13243            .map(|line| format!("X{}", &line[1..]))
13244            .collect::<Vec<_>>()
13245            .join("\n")
13246    }
13247}
13248
13249#[gpui::test]
13250async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13251    init_test(cx, |_| {});
13252
13253    let cols = 4;
13254    let rows = 10;
13255    let sample_text_1 = sample_text(rows, cols, 'a');
13256    assert_eq!(
13257        sample_text_1,
13258        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13259    );
13260    let sample_text_2 = sample_text(rows, cols, 'l');
13261    assert_eq!(
13262        sample_text_2,
13263        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13264    );
13265    let sample_text_3 = sample_text(rows, cols, 'v');
13266    assert_eq!(
13267        sample_text_3,
13268        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13269    );
13270
13271    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13272    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13273    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13274
13275    let multi_buffer = cx.new(|cx| {
13276        let mut multibuffer = MultiBuffer::new(ReadWrite);
13277        multibuffer.push_excerpts(
13278            buffer_1.clone(),
13279            [
13280                ExcerptRange {
13281                    context: Point::new(0, 0)..Point::new(3, 0),
13282                    primary: None,
13283                },
13284                ExcerptRange {
13285                    context: Point::new(5, 0)..Point::new(7, 0),
13286                    primary: None,
13287                },
13288                ExcerptRange {
13289                    context: Point::new(9, 0)..Point::new(10, 4),
13290                    primary: None,
13291                },
13292            ],
13293            cx,
13294        );
13295        multibuffer.push_excerpts(
13296            buffer_2.clone(),
13297            [
13298                ExcerptRange {
13299                    context: Point::new(0, 0)..Point::new(3, 0),
13300                    primary: None,
13301                },
13302                ExcerptRange {
13303                    context: Point::new(5, 0)..Point::new(7, 0),
13304                    primary: None,
13305                },
13306                ExcerptRange {
13307                    context: Point::new(9, 0)..Point::new(10, 4),
13308                    primary: None,
13309                },
13310            ],
13311            cx,
13312        );
13313        multibuffer.push_excerpts(
13314            buffer_3.clone(),
13315            [
13316                ExcerptRange {
13317                    context: Point::new(0, 0)..Point::new(3, 0),
13318                    primary: None,
13319                },
13320                ExcerptRange {
13321                    context: Point::new(5, 0)..Point::new(7, 0),
13322                    primary: None,
13323                },
13324                ExcerptRange {
13325                    context: Point::new(9, 0)..Point::new(10, 4),
13326                    primary: None,
13327                },
13328            ],
13329            cx,
13330        );
13331        multibuffer
13332    });
13333
13334    let fs = FakeFs::new(cx.executor());
13335    fs.insert_tree(
13336        "/a",
13337        json!({
13338            "main.rs": sample_text_1,
13339            "other.rs": sample_text_2,
13340            "lib.rs": sample_text_3,
13341        }),
13342    )
13343    .await;
13344    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13345    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13346    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13347    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13348        Editor::new(
13349            EditorMode::Full,
13350            multi_buffer,
13351            Some(project.clone()),
13352            true,
13353            window,
13354            cx,
13355        )
13356    });
13357    let multibuffer_item_id = workspace
13358        .update(cx, |workspace, window, cx| {
13359            assert!(
13360                workspace.active_item(cx).is_none(),
13361                "active item should be None before the first item is added"
13362            );
13363            workspace.add_item_to_active_pane(
13364                Box::new(multi_buffer_editor.clone()),
13365                None,
13366                true,
13367                window,
13368                cx,
13369            );
13370            let active_item = workspace
13371                .active_item(cx)
13372                .expect("should have an active item after adding the multi buffer");
13373            assert!(
13374                !active_item.is_singleton(cx),
13375                "A multi buffer was expected to active after adding"
13376            );
13377            active_item.item_id()
13378        })
13379        .unwrap();
13380    cx.executor().run_until_parked();
13381
13382    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13383        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13384            s.select_ranges(Some(1..2))
13385        });
13386        editor.open_excerpts(&OpenExcerpts, window, cx);
13387    });
13388    cx.executor().run_until_parked();
13389    let first_item_id = workspace
13390        .update(cx, |workspace, window, cx| {
13391            let active_item = workspace
13392                .active_item(cx)
13393                .expect("should have an active item after navigating into the 1st buffer");
13394            let first_item_id = active_item.item_id();
13395            assert_ne!(
13396                first_item_id, multibuffer_item_id,
13397                "Should navigate into the 1st buffer and activate it"
13398            );
13399            assert!(
13400                active_item.is_singleton(cx),
13401                "New active item should be a singleton buffer"
13402            );
13403            assert_eq!(
13404                active_item
13405                    .act_as::<Editor>(cx)
13406                    .expect("should have navigated into an editor for the 1st buffer")
13407                    .read(cx)
13408                    .text(cx),
13409                sample_text_1
13410            );
13411
13412            workspace
13413                .go_back(workspace.active_pane().downgrade(), window, cx)
13414                .detach_and_log_err(cx);
13415
13416            first_item_id
13417        })
13418        .unwrap();
13419    cx.executor().run_until_parked();
13420    workspace
13421        .update(cx, |workspace, _, cx| {
13422            let active_item = workspace
13423                .active_item(cx)
13424                .expect("should have an active item after navigating back");
13425            assert_eq!(
13426                active_item.item_id(),
13427                multibuffer_item_id,
13428                "Should navigate back to the multi buffer"
13429            );
13430            assert!(!active_item.is_singleton(cx));
13431        })
13432        .unwrap();
13433
13434    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13435        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13436            s.select_ranges(Some(39..40))
13437        });
13438        editor.open_excerpts(&OpenExcerpts, window, cx);
13439    });
13440    cx.executor().run_until_parked();
13441    let second_item_id = workspace
13442        .update(cx, |workspace, window, cx| {
13443            let active_item = workspace
13444                .active_item(cx)
13445                .expect("should have an active item after navigating into the 2nd buffer");
13446            let second_item_id = active_item.item_id();
13447            assert_ne!(
13448                second_item_id, multibuffer_item_id,
13449                "Should navigate away from the multibuffer"
13450            );
13451            assert_ne!(
13452                second_item_id, first_item_id,
13453                "Should navigate into the 2nd buffer and activate it"
13454            );
13455            assert!(
13456                active_item.is_singleton(cx),
13457                "New active item should be a singleton buffer"
13458            );
13459            assert_eq!(
13460                active_item
13461                    .act_as::<Editor>(cx)
13462                    .expect("should have navigated into an editor")
13463                    .read(cx)
13464                    .text(cx),
13465                sample_text_2
13466            );
13467
13468            workspace
13469                .go_back(workspace.active_pane().downgrade(), window, cx)
13470                .detach_and_log_err(cx);
13471
13472            second_item_id
13473        })
13474        .unwrap();
13475    cx.executor().run_until_parked();
13476    workspace
13477        .update(cx, |workspace, _, cx| {
13478            let active_item = workspace
13479                .active_item(cx)
13480                .expect("should have an active item after navigating back from the 2nd buffer");
13481            assert_eq!(
13482                active_item.item_id(),
13483                multibuffer_item_id,
13484                "Should navigate back from the 2nd buffer to the multi buffer"
13485            );
13486            assert!(!active_item.is_singleton(cx));
13487        })
13488        .unwrap();
13489
13490    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13491        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13492            s.select_ranges(Some(70..70))
13493        });
13494        editor.open_excerpts(&OpenExcerpts, window, cx);
13495    });
13496    cx.executor().run_until_parked();
13497    workspace
13498        .update(cx, |workspace, window, cx| {
13499            let active_item = workspace
13500                .active_item(cx)
13501                .expect("should have an active item after navigating into the 3rd buffer");
13502            let third_item_id = active_item.item_id();
13503            assert_ne!(
13504                third_item_id, multibuffer_item_id,
13505                "Should navigate into the 3rd buffer and activate it"
13506            );
13507            assert_ne!(third_item_id, first_item_id);
13508            assert_ne!(third_item_id, second_item_id);
13509            assert!(
13510                active_item.is_singleton(cx),
13511                "New active item should be a singleton buffer"
13512            );
13513            assert_eq!(
13514                active_item
13515                    .act_as::<Editor>(cx)
13516                    .expect("should have navigated into an editor")
13517                    .read(cx)
13518                    .text(cx),
13519                sample_text_3
13520            );
13521
13522            workspace
13523                .go_back(workspace.active_pane().downgrade(), window, cx)
13524                .detach_and_log_err(cx);
13525        })
13526        .unwrap();
13527    cx.executor().run_until_parked();
13528    workspace
13529        .update(cx, |workspace, _, cx| {
13530            let active_item = workspace
13531                .active_item(cx)
13532                .expect("should have an active item after navigating back from the 3rd buffer");
13533            assert_eq!(
13534                active_item.item_id(),
13535                multibuffer_item_id,
13536                "Should navigate back from the 3rd buffer to the multi buffer"
13537            );
13538            assert!(!active_item.is_singleton(cx));
13539        })
13540        .unwrap();
13541}
13542
13543#[gpui::test]
13544async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13545    init_test(cx, |_| {});
13546
13547    let mut cx = EditorTestContext::new(cx).await;
13548
13549    let diff_base = r#"
13550        use some::mod;
13551
13552        const A: u32 = 42;
13553
13554        fn main() {
13555            println!("hello");
13556
13557            println!("world");
13558        }
13559        "#
13560    .unindent();
13561
13562    cx.set_state(
13563        &r#"
13564        use some::modified;
13565
13566        ˇ
13567        fn main() {
13568            println!("hello there");
13569
13570            println!("around the");
13571            println!("world");
13572        }
13573        "#
13574        .unindent(),
13575    );
13576
13577    cx.set_head_text(&diff_base);
13578    executor.run_until_parked();
13579
13580    cx.update_editor(|editor, window, cx| {
13581        editor.go_to_next_hunk(&GoToHunk, window, cx);
13582        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13583    });
13584    executor.run_until_parked();
13585    cx.assert_state_with_diff(
13586        r#"
13587          use some::modified;
13588
13589
13590          fn main() {
13591        -     println!("hello");
13592        + ˇ    println!("hello there");
13593
13594              println!("around the");
13595              println!("world");
13596          }
13597        "#
13598        .unindent(),
13599    );
13600
13601    cx.update_editor(|editor, window, cx| {
13602        for _ in 0..2 {
13603            editor.go_to_next_hunk(&GoToHunk, window, cx);
13604            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13605        }
13606    });
13607    executor.run_until_parked();
13608    cx.assert_state_with_diff(
13609        r#"
13610        - use some::mod;
13611        + ˇuse some::modified;
13612
13613
13614          fn main() {
13615        -     println!("hello");
13616        +     println!("hello there");
13617
13618        +     println!("around the");
13619              println!("world");
13620          }
13621        "#
13622        .unindent(),
13623    );
13624
13625    cx.update_editor(|editor, window, cx| {
13626        editor.go_to_next_hunk(&GoToHunk, window, cx);
13627        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13628    });
13629    executor.run_until_parked();
13630    cx.assert_state_with_diff(
13631        r#"
13632        - use some::mod;
13633        + use some::modified;
13634
13635        - const A: u32 = 42;
13636          ˇ
13637          fn main() {
13638        -     println!("hello");
13639        +     println!("hello there");
13640
13641        +     println!("around the");
13642              println!("world");
13643          }
13644        "#
13645        .unindent(),
13646    );
13647
13648    cx.update_editor(|editor, window, cx| {
13649        editor.cancel(&Cancel, window, cx);
13650    });
13651
13652    cx.assert_state_with_diff(
13653        r#"
13654          use some::modified;
13655
13656          ˇ
13657          fn main() {
13658              println!("hello there");
13659
13660              println!("around the");
13661              println!("world");
13662          }
13663        "#
13664        .unindent(),
13665    );
13666}
13667
13668#[gpui::test]
13669async fn test_diff_base_change_with_expanded_diff_hunks(
13670    executor: BackgroundExecutor,
13671    cx: &mut TestAppContext,
13672) {
13673    init_test(cx, |_| {});
13674
13675    let mut cx = EditorTestContext::new(cx).await;
13676
13677    let diff_base = r#"
13678        use some::mod1;
13679        use some::mod2;
13680
13681        const A: u32 = 42;
13682        const B: u32 = 42;
13683        const C: u32 = 42;
13684
13685        fn main() {
13686            println!("hello");
13687
13688            println!("world");
13689        }
13690        "#
13691    .unindent();
13692
13693    cx.set_state(
13694        &r#"
13695        use some::mod2;
13696
13697        const A: u32 = 42;
13698        const C: u32 = 42;
13699
13700        fn main(ˇ) {
13701            //println!("hello");
13702
13703            println!("world");
13704            //
13705            //
13706        }
13707        "#
13708        .unindent(),
13709    );
13710
13711    cx.set_head_text(&diff_base);
13712    executor.run_until_parked();
13713
13714    cx.update_editor(|editor, window, cx| {
13715        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13716    });
13717    executor.run_until_parked();
13718    cx.assert_state_with_diff(
13719        r#"
13720        - use some::mod1;
13721          use some::mod2;
13722
13723          const A: u32 = 42;
13724        - const B: u32 = 42;
13725          const C: u32 = 42;
13726
13727          fn main(ˇ) {
13728        -     println!("hello");
13729        +     //println!("hello");
13730
13731              println!("world");
13732        +     //
13733        +     //
13734          }
13735        "#
13736        .unindent(),
13737    );
13738
13739    cx.set_head_text("new diff base!");
13740    executor.run_until_parked();
13741    cx.assert_state_with_diff(
13742        r#"
13743        - new diff base!
13744        + use some::mod2;
13745        +
13746        + const A: u32 = 42;
13747        + const C: u32 = 42;
13748        +
13749        + fn main(ˇ) {
13750        +     //println!("hello");
13751        +
13752        +     println!("world");
13753        +     //
13754        +     //
13755        + }
13756        "#
13757        .unindent(),
13758    );
13759}
13760
13761#[gpui::test]
13762async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13763    init_test(cx, |_| {});
13764
13765    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13766    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13767    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13768    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13769    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13770    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13771
13772    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13773    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13774    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13775
13776    let multi_buffer = cx.new(|cx| {
13777        let mut multibuffer = MultiBuffer::new(ReadWrite);
13778        multibuffer.push_excerpts(
13779            buffer_1.clone(),
13780            [
13781                ExcerptRange {
13782                    context: Point::new(0, 0)..Point::new(3, 0),
13783                    primary: None,
13784                },
13785                ExcerptRange {
13786                    context: Point::new(5, 0)..Point::new(7, 0),
13787                    primary: None,
13788                },
13789                ExcerptRange {
13790                    context: Point::new(9, 0)..Point::new(10, 3),
13791                    primary: None,
13792                },
13793            ],
13794            cx,
13795        );
13796        multibuffer.push_excerpts(
13797            buffer_2.clone(),
13798            [
13799                ExcerptRange {
13800                    context: Point::new(0, 0)..Point::new(3, 0),
13801                    primary: None,
13802                },
13803                ExcerptRange {
13804                    context: Point::new(5, 0)..Point::new(7, 0),
13805                    primary: None,
13806                },
13807                ExcerptRange {
13808                    context: Point::new(9, 0)..Point::new(10, 3),
13809                    primary: None,
13810                },
13811            ],
13812            cx,
13813        );
13814        multibuffer.push_excerpts(
13815            buffer_3.clone(),
13816            [
13817                ExcerptRange {
13818                    context: Point::new(0, 0)..Point::new(3, 0),
13819                    primary: None,
13820                },
13821                ExcerptRange {
13822                    context: Point::new(5, 0)..Point::new(7, 0),
13823                    primary: None,
13824                },
13825                ExcerptRange {
13826                    context: Point::new(9, 0)..Point::new(10, 3),
13827                    primary: None,
13828                },
13829            ],
13830            cx,
13831        );
13832        multibuffer
13833    });
13834
13835    let editor = cx.add_window(|window, cx| {
13836        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13837    });
13838    editor
13839        .update(cx, |editor, _window, cx| {
13840            for (buffer, diff_base) in [
13841                (buffer_1.clone(), file_1_old),
13842                (buffer_2.clone(), file_2_old),
13843                (buffer_3.clone(), file_3_old),
13844            ] {
13845                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13846                editor
13847                    .buffer
13848                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13849            }
13850        })
13851        .unwrap();
13852
13853    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13854    cx.run_until_parked();
13855
13856    cx.assert_editor_state(
13857        &"
13858            ˇaaa
13859            ccc
13860            ddd
13861
13862            ggg
13863            hhh
13864
13865
13866            lll
13867            mmm
13868            NNN
13869
13870            qqq
13871            rrr
13872
13873            uuu
13874            111
13875            222
13876            333
13877
13878            666
13879            777
13880
13881            000
13882            !!!"
13883        .unindent(),
13884    );
13885
13886    cx.update_editor(|editor, window, cx| {
13887        editor.select_all(&SelectAll, window, cx);
13888        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13889    });
13890    cx.executor().run_until_parked();
13891
13892    cx.assert_state_with_diff(
13893        "
13894            «aaa
13895          - bbb
13896            ccc
13897            ddd
13898
13899            ggg
13900            hhh
13901
13902
13903            lll
13904            mmm
13905          - nnn
13906          + NNN
13907
13908            qqq
13909            rrr
13910
13911            uuu
13912            111
13913            222
13914            333
13915
13916          + 666
13917            777
13918
13919            000
13920            !!!ˇ»"
13921            .unindent(),
13922    );
13923}
13924
13925#[gpui::test]
13926async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13927    init_test(cx, |_| {});
13928
13929    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13930    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13931
13932    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13933    let multi_buffer = cx.new(|cx| {
13934        let mut multibuffer = MultiBuffer::new(ReadWrite);
13935        multibuffer.push_excerpts(
13936            buffer.clone(),
13937            [
13938                ExcerptRange {
13939                    context: Point::new(0, 0)..Point::new(2, 0),
13940                    primary: None,
13941                },
13942                ExcerptRange {
13943                    context: Point::new(4, 0)..Point::new(7, 0),
13944                    primary: None,
13945                },
13946                ExcerptRange {
13947                    context: Point::new(9, 0)..Point::new(10, 0),
13948                    primary: None,
13949                },
13950            ],
13951            cx,
13952        );
13953        multibuffer
13954    });
13955
13956    let editor = cx.add_window(|window, cx| {
13957        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13958    });
13959    editor
13960        .update(cx, |editor, _window, cx| {
13961            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13962            editor
13963                .buffer
13964                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13965        })
13966        .unwrap();
13967
13968    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13969    cx.run_until_parked();
13970
13971    cx.update_editor(|editor, window, cx| {
13972        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13973    });
13974    cx.executor().run_until_parked();
13975
13976    // When the start of a hunk coincides with the start of its excerpt,
13977    // the hunk is expanded. When the start of a a hunk is earlier than
13978    // the start of its excerpt, the hunk is not expanded.
13979    cx.assert_state_with_diff(
13980        "
13981            ˇaaa
13982          - bbb
13983          + BBB
13984
13985          - ddd
13986          - eee
13987          + DDD
13988          + EEE
13989            fff
13990
13991            iii
13992        "
13993        .unindent(),
13994    );
13995}
13996
13997#[gpui::test]
13998async fn test_edits_around_expanded_insertion_hunks(
13999    executor: BackgroundExecutor,
14000    cx: &mut TestAppContext,
14001) {
14002    init_test(cx, |_| {});
14003
14004    let mut cx = EditorTestContext::new(cx).await;
14005
14006    let diff_base = r#"
14007        use some::mod1;
14008        use some::mod2;
14009
14010        const A: u32 = 42;
14011
14012        fn main() {
14013            println!("hello");
14014
14015            println!("world");
14016        }
14017        "#
14018    .unindent();
14019    executor.run_until_parked();
14020    cx.set_state(
14021        &r#"
14022        use some::mod1;
14023        use some::mod2;
14024
14025        const A: u32 = 42;
14026        const B: u32 = 42;
14027        const C: u32 = 42;
14028        ˇ
14029
14030        fn main() {
14031            println!("hello");
14032
14033            println!("world");
14034        }
14035        "#
14036        .unindent(),
14037    );
14038
14039    cx.set_head_text(&diff_base);
14040    executor.run_until_parked();
14041
14042    cx.update_editor(|editor, window, cx| {
14043        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14044    });
14045    executor.run_until_parked();
14046
14047    cx.assert_state_with_diff(
14048        r#"
14049        use some::mod1;
14050        use some::mod2;
14051
14052        const A: u32 = 42;
14053      + const B: u32 = 42;
14054      + const C: u32 = 42;
14055      + ˇ
14056
14057        fn main() {
14058            println!("hello");
14059
14060            println!("world");
14061        }
14062      "#
14063        .unindent(),
14064    );
14065
14066    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14067    executor.run_until_parked();
14068
14069    cx.assert_state_with_diff(
14070        r#"
14071        use some::mod1;
14072        use some::mod2;
14073
14074        const A: u32 = 42;
14075      + const B: u32 = 42;
14076      + const C: u32 = 42;
14077      + const D: u32 = 42;
14078      + ˇ
14079
14080        fn main() {
14081            println!("hello");
14082
14083            println!("world");
14084        }
14085      "#
14086        .unindent(),
14087    );
14088
14089    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14090    executor.run_until_parked();
14091
14092    cx.assert_state_with_diff(
14093        r#"
14094        use some::mod1;
14095        use some::mod2;
14096
14097        const A: u32 = 42;
14098      + const B: u32 = 42;
14099      + const C: u32 = 42;
14100      + const D: u32 = 42;
14101      + const E: u32 = 42;
14102      + ˇ
14103
14104        fn main() {
14105            println!("hello");
14106
14107            println!("world");
14108        }
14109      "#
14110        .unindent(),
14111    );
14112
14113    cx.update_editor(|editor, window, cx| {
14114        editor.delete_line(&DeleteLine, window, cx);
14115    });
14116    executor.run_until_parked();
14117
14118    cx.assert_state_with_diff(
14119        r#"
14120        use some::mod1;
14121        use some::mod2;
14122
14123        const A: u32 = 42;
14124      + const B: u32 = 42;
14125      + const C: u32 = 42;
14126      + const D: u32 = 42;
14127      + const E: u32 = 42;
14128        ˇ
14129        fn main() {
14130            println!("hello");
14131
14132            println!("world");
14133        }
14134      "#
14135        .unindent(),
14136    );
14137
14138    cx.update_editor(|editor, window, cx| {
14139        editor.move_up(&MoveUp, window, cx);
14140        editor.delete_line(&DeleteLine, window, cx);
14141        editor.move_up(&MoveUp, window, cx);
14142        editor.delete_line(&DeleteLine, window, cx);
14143        editor.move_up(&MoveUp, window, cx);
14144        editor.delete_line(&DeleteLine, window, cx);
14145    });
14146    executor.run_until_parked();
14147    cx.assert_state_with_diff(
14148        r#"
14149        use some::mod1;
14150        use some::mod2;
14151
14152        const A: u32 = 42;
14153      + const B: u32 = 42;
14154        ˇ
14155        fn main() {
14156            println!("hello");
14157
14158            println!("world");
14159        }
14160      "#
14161        .unindent(),
14162    );
14163
14164    cx.update_editor(|editor, window, cx| {
14165        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14166        editor.delete_line(&DeleteLine, window, cx);
14167    });
14168    executor.run_until_parked();
14169    cx.assert_state_with_diff(
14170        r#"
14171        ˇ
14172        fn main() {
14173            println!("hello");
14174
14175            println!("world");
14176        }
14177      "#
14178        .unindent(),
14179    );
14180}
14181
14182#[gpui::test]
14183async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14184    init_test(cx, |_| {});
14185
14186    let mut cx = EditorTestContext::new(cx).await;
14187    cx.set_head_text(indoc! { "
14188        one
14189        two
14190        three
14191        four
14192        five
14193        "
14194    });
14195    cx.set_state(indoc! { "
14196        one
14197        ˇthree
14198        five
14199    "});
14200    cx.run_until_parked();
14201    cx.update_editor(|editor, window, cx| {
14202        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14203    });
14204    cx.assert_state_with_diff(
14205        indoc! { "
14206        one
14207      - two
14208        ˇthree
14209      - four
14210        five
14211    "}
14212        .to_string(),
14213    );
14214    cx.update_editor(|editor, window, cx| {
14215        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14216    });
14217
14218    cx.assert_state_with_diff(
14219        indoc! { "
14220        one
14221        ˇthree
14222        five
14223    "}
14224        .to_string(),
14225    );
14226
14227    cx.set_state(indoc! { "
14228        one
14229        ˇTWO
14230        three
14231        four
14232        five
14233    "});
14234    cx.run_until_parked();
14235    cx.update_editor(|editor, window, cx| {
14236        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14237    });
14238
14239    cx.assert_state_with_diff(
14240        indoc! { "
14241            one
14242          - two
14243          + ˇTWO
14244            three
14245            four
14246            five
14247        "}
14248        .to_string(),
14249    );
14250    cx.update_editor(|editor, window, cx| {
14251        editor.move_up(&Default::default(), window, cx);
14252        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14253    });
14254    cx.assert_state_with_diff(
14255        indoc! { "
14256            one
14257            ˇTWO
14258            three
14259            four
14260            five
14261        "}
14262        .to_string(),
14263    );
14264}
14265
14266#[gpui::test]
14267async fn test_edits_around_expanded_deletion_hunks(
14268    executor: BackgroundExecutor,
14269    cx: &mut TestAppContext,
14270) {
14271    init_test(cx, |_| {});
14272
14273    let mut cx = EditorTestContext::new(cx).await;
14274
14275    let diff_base = r#"
14276        use some::mod1;
14277        use some::mod2;
14278
14279        const A: u32 = 42;
14280        const B: u32 = 42;
14281        const C: u32 = 42;
14282
14283
14284        fn main() {
14285            println!("hello");
14286
14287            println!("world");
14288        }
14289    "#
14290    .unindent();
14291    executor.run_until_parked();
14292    cx.set_state(
14293        &r#"
14294        use some::mod1;
14295        use some::mod2;
14296
14297        ˇconst B: u32 = 42;
14298        const C: u32 = 42;
14299
14300
14301        fn main() {
14302            println!("hello");
14303
14304            println!("world");
14305        }
14306        "#
14307        .unindent(),
14308    );
14309
14310    cx.set_head_text(&diff_base);
14311    executor.run_until_parked();
14312
14313    cx.update_editor(|editor, window, cx| {
14314        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14315    });
14316    executor.run_until_parked();
14317
14318    cx.assert_state_with_diff(
14319        r#"
14320        use some::mod1;
14321        use some::mod2;
14322
14323      - const A: u32 = 42;
14324        ˇconst B: u32 = 42;
14325        const C: u32 = 42;
14326
14327
14328        fn main() {
14329            println!("hello");
14330
14331            println!("world");
14332        }
14333      "#
14334        .unindent(),
14335    );
14336
14337    cx.update_editor(|editor, window, cx| {
14338        editor.delete_line(&DeleteLine, window, cx);
14339    });
14340    executor.run_until_parked();
14341    cx.assert_state_with_diff(
14342        r#"
14343        use some::mod1;
14344        use some::mod2;
14345
14346      - const A: u32 = 42;
14347      - const B: u32 = 42;
14348        ˇconst C: u32 = 42;
14349
14350
14351        fn main() {
14352            println!("hello");
14353
14354            println!("world");
14355        }
14356      "#
14357        .unindent(),
14358    );
14359
14360    cx.update_editor(|editor, window, cx| {
14361        editor.delete_line(&DeleteLine, window, cx);
14362    });
14363    executor.run_until_parked();
14364    cx.assert_state_with_diff(
14365        r#"
14366        use some::mod1;
14367        use some::mod2;
14368
14369      - const A: u32 = 42;
14370      - const B: u32 = 42;
14371      - const C: u32 = 42;
14372        ˇ
14373
14374        fn main() {
14375            println!("hello");
14376
14377            println!("world");
14378        }
14379      "#
14380        .unindent(),
14381    );
14382
14383    cx.update_editor(|editor, window, cx| {
14384        editor.handle_input("replacement", window, cx);
14385    });
14386    executor.run_until_parked();
14387    cx.assert_state_with_diff(
14388        r#"
14389        use some::mod1;
14390        use some::mod2;
14391
14392      - const A: u32 = 42;
14393      - const B: u32 = 42;
14394      - const C: u32 = 42;
14395      -
14396      + replacementˇ
14397
14398        fn main() {
14399            println!("hello");
14400
14401            println!("world");
14402        }
14403      "#
14404        .unindent(),
14405    );
14406}
14407
14408#[gpui::test]
14409async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14410    init_test(cx, |_| {});
14411
14412    let mut cx = EditorTestContext::new(cx).await;
14413
14414    let base_text = r#"
14415        one
14416        two
14417        three
14418        four
14419        five
14420    "#
14421    .unindent();
14422    executor.run_until_parked();
14423    cx.set_state(
14424        &r#"
14425        one
14426        two
14427        fˇour
14428        five
14429        "#
14430        .unindent(),
14431    );
14432
14433    cx.set_head_text(&base_text);
14434    executor.run_until_parked();
14435
14436    cx.update_editor(|editor, window, cx| {
14437        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14438    });
14439    executor.run_until_parked();
14440
14441    cx.assert_state_with_diff(
14442        r#"
14443          one
14444          two
14445        - three
14446          fˇour
14447          five
14448        "#
14449        .unindent(),
14450    );
14451
14452    cx.update_editor(|editor, window, cx| {
14453        editor.backspace(&Backspace, window, cx);
14454        editor.backspace(&Backspace, window, cx);
14455    });
14456    executor.run_until_parked();
14457    cx.assert_state_with_diff(
14458        r#"
14459          one
14460          two
14461        - threeˇ
14462        - four
14463        + our
14464          five
14465        "#
14466        .unindent(),
14467    );
14468}
14469
14470#[gpui::test]
14471async fn test_edit_after_expanded_modification_hunk(
14472    executor: BackgroundExecutor,
14473    cx: &mut TestAppContext,
14474) {
14475    init_test(cx, |_| {});
14476
14477    let mut cx = EditorTestContext::new(cx).await;
14478
14479    let diff_base = r#"
14480        use some::mod1;
14481        use some::mod2;
14482
14483        const A: u32 = 42;
14484        const B: u32 = 42;
14485        const C: u32 = 42;
14486        const D: u32 = 42;
14487
14488
14489        fn main() {
14490            println!("hello");
14491
14492            println!("world");
14493        }"#
14494    .unindent();
14495
14496    cx.set_state(
14497        &r#"
14498        use some::mod1;
14499        use some::mod2;
14500
14501        const A: u32 = 42;
14502        const B: u32 = 42;
14503        const C: u32 = 43ˇ
14504        const D: u32 = 42;
14505
14506
14507        fn main() {
14508            println!("hello");
14509
14510            println!("world");
14511        }"#
14512        .unindent(),
14513    );
14514
14515    cx.set_head_text(&diff_base);
14516    executor.run_until_parked();
14517    cx.update_editor(|editor, window, cx| {
14518        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14519    });
14520    executor.run_until_parked();
14521
14522    cx.assert_state_with_diff(
14523        r#"
14524        use some::mod1;
14525        use some::mod2;
14526
14527        const A: u32 = 42;
14528        const B: u32 = 42;
14529      - const C: u32 = 42;
14530      + const C: u32 = 43ˇ
14531        const D: u32 = 42;
14532
14533
14534        fn main() {
14535            println!("hello");
14536
14537            println!("world");
14538        }"#
14539        .unindent(),
14540    );
14541
14542    cx.update_editor(|editor, window, cx| {
14543        editor.handle_input("\nnew_line\n", window, cx);
14544    });
14545    executor.run_until_parked();
14546
14547    cx.assert_state_with_diff(
14548        r#"
14549        use some::mod1;
14550        use some::mod2;
14551
14552        const A: u32 = 42;
14553        const B: u32 = 42;
14554      - const C: u32 = 42;
14555      + const C: u32 = 43
14556      + new_line
14557      + ˇ
14558        const D: u32 = 42;
14559
14560
14561        fn main() {
14562            println!("hello");
14563
14564            println!("world");
14565        }"#
14566        .unindent(),
14567    );
14568}
14569
14570#[gpui::test]
14571async fn test_stage_and_unstage_added_file_hunk(
14572    executor: BackgroundExecutor,
14573    cx: &mut TestAppContext,
14574) {
14575    init_test(cx, |_| {});
14576
14577    let mut cx = EditorTestContext::new(cx).await;
14578    cx.update_editor(|editor, _, cx| {
14579        editor.set_expand_all_diff_hunks(cx);
14580    });
14581
14582    let working_copy = r#"
14583            ˇfn main() {
14584                println!("hello, world!");
14585            }
14586        "#
14587    .unindent();
14588
14589    cx.set_state(&working_copy);
14590    executor.run_until_parked();
14591
14592    cx.assert_state_with_diff(
14593        r#"
14594            + ˇfn main() {
14595            +     println!("hello, world!");
14596            + }
14597        "#
14598        .unindent(),
14599    );
14600    cx.assert_index_text(None);
14601
14602    cx.update_editor(|editor, window, cx| {
14603        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14604    });
14605    executor.run_until_parked();
14606    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14607    cx.assert_state_with_diff(
14608        r#"
14609            + ˇfn main() {
14610            +     println!("hello, world!");
14611            + }
14612        "#
14613        .unindent(),
14614    );
14615
14616    cx.update_editor(|editor, window, cx| {
14617        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14618    });
14619    executor.run_until_parked();
14620    cx.assert_index_text(None);
14621}
14622
14623async fn setup_indent_guides_editor(
14624    text: &str,
14625    cx: &mut TestAppContext,
14626) -> (BufferId, EditorTestContext) {
14627    init_test(cx, |_| {});
14628
14629    let mut cx = EditorTestContext::new(cx).await;
14630
14631    let buffer_id = cx.update_editor(|editor, window, cx| {
14632        editor.set_text(text, window, cx);
14633        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14634
14635        buffer_ids[0]
14636    });
14637
14638    (buffer_id, cx)
14639}
14640
14641fn assert_indent_guides(
14642    range: Range<u32>,
14643    expected: Vec<IndentGuide>,
14644    active_indices: Option<Vec<usize>>,
14645    cx: &mut EditorTestContext,
14646) {
14647    let indent_guides = cx.update_editor(|editor, window, cx| {
14648        let snapshot = editor.snapshot(window, cx).display_snapshot;
14649        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14650            editor,
14651            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14652            true,
14653            &snapshot,
14654            cx,
14655        );
14656
14657        indent_guides.sort_by(|a, b| {
14658            a.depth.cmp(&b.depth).then(
14659                a.start_row
14660                    .cmp(&b.start_row)
14661                    .then(a.end_row.cmp(&b.end_row)),
14662            )
14663        });
14664        indent_guides
14665    });
14666
14667    if let Some(expected) = active_indices {
14668        let active_indices = cx.update_editor(|editor, window, cx| {
14669            let snapshot = editor.snapshot(window, cx).display_snapshot;
14670            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14671        });
14672
14673        assert_eq!(
14674            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14675            expected,
14676            "Active indent guide indices do not match"
14677        );
14678    }
14679
14680    assert_eq!(indent_guides, expected, "Indent guides do not match");
14681}
14682
14683fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14684    IndentGuide {
14685        buffer_id,
14686        start_row: MultiBufferRow(start_row),
14687        end_row: MultiBufferRow(end_row),
14688        depth,
14689        tab_size: 4,
14690        settings: IndentGuideSettings {
14691            enabled: true,
14692            line_width: 1,
14693            active_line_width: 1,
14694            ..Default::default()
14695        },
14696    }
14697}
14698
14699#[gpui::test]
14700async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14701    let (buffer_id, mut cx) = setup_indent_guides_editor(
14702        &"
14703    fn main() {
14704        let a = 1;
14705    }"
14706        .unindent(),
14707        cx,
14708    )
14709    .await;
14710
14711    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14712}
14713
14714#[gpui::test]
14715async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14716    let (buffer_id, mut cx) = setup_indent_guides_editor(
14717        &"
14718    fn main() {
14719        let a = 1;
14720        let b = 2;
14721    }"
14722        .unindent(),
14723        cx,
14724    )
14725    .await;
14726
14727    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14728}
14729
14730#[gpui::test]
14731async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14732    let (buffer_id, mut cx) = setup_indent_guides_editor(
14733        &"
14734    fn main() {
14735        let a = 1;
14736        if a == 3 {
14737            let b = 2;
14738        } else {
14739            let c = 3;
14740        }
14741    }"
14742        .unindent(),
14743        cx,
14744    )
14745    .await;
14746
14747    assert_indent_guides(
14748        0..8,
14749        vec![
14750            indent_guide(buffer_id, 1, 6, 0),
14751            indent_guide(buffer_id, 3, 3, 1),
14752            indent_guide(buffer_id, 5, 5, 1),
14753        ],
14754        None,
14755        &mut cx,
14756    );
14757}
14758
14759#[gpui::test]
14760async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14761    let (buffer_id, mut cx) = setup_indent_guides_editor(
14762        &"
14763    fn main() {
14764        let a = 1;
14765            let b = 2;
14766        let c = 3;
14767    }"
14768        .unindent(),
14769        cx,
14770    )
14771    .await;
14772
14773    assert_indent_guides(
14774        0..5,
14775        vec![
14776            indent_guide(buffer_id, 1, 3, 0),
14777            indent_guide(buffer_id, 2, 2, 1),
14778        ],
14779        None,
14780        &mut cx,
14781    );
14782}
14783
14784#[gpui::test]
14785async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14786    let (buffer_id, mut cx) = setup_indent_guides_editor(
14787        &"
14788        fn main() {
14789            let a = 1;
14790
14791            let c = 3;
14792        }"
14793        .unindent(),
14794        cx,
14795    )
14796    .await;
14797
14798    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14799}
14800
14801#[gpui::test]
14802async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14803    let (buffer_id, mut cx) = setup_indent_guides_editor(
14804        &"
14805        fn main() {
14806            let a = 1;
14807
14808            let c = 3;
14809
14810            if a == 3 {
14811                let b = 2;
14812            } else {
14813                let c = 3;
14814            }
14815        }"
14816        .unindent(),
14817        cx,
14818    )
14819    .await;
14820
14821    assert_indent_guides(
14822        0..11,
14823        vec![
14824            indent_guide(buffer_id, 1, 9, 0),
14825            indent_guide(buffer_id, 6, 6, 1),
14826            indent_guide(buffer_id, 8, 8, 1),
14827        ],
14828        None,
14829        &mut cx,
14830    );
14831}
14832
14833#[gpui::test]
14834async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14835    let (buffer_id, mut cx) = setup_indent_guides_editor(
14836        &"
14837        fn main() {
14838            let a = 1;
14839
14840            let c = 3;
14841
14842            if a == 3 {
14843                let b = 2;
14844            } else {
14845                let c = 3;
14846            }
14847        }"
14848        .unindent(),
14849        cx,
14850    )
14851    .await;
14852
14853    assert_indent_guides(
14854        1..11,
14855        vec![
14856            indent_guide(buffer_id, 1, 9, 0),
14857            indent_guide(buffer_id, 6, 6, 1),
14858            indent_guide(buffer_id, 8, 8, 1),
14859        ],
14860        None,
14861        &mut cx,
14862    );
14863}
14864
14865#[gpui::test]
14866async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14867    let (buffer_id, mut cx) = setup_indent_guides_editor(
14868        &"
14869        fn main() {
14870            let a = 1;
14871
14872            let c = 3;
14873
14874            if a == 3 {
14875                let b = 2;
14876            } else {
14877                let c = 3;
14878            }
14879        }"
14880        .unindent(),
14881        cx,
14882    )
14883    .await;
14884
14885    assert_indent_guides(
14886        1..10,
14887        vec![
14888            indent_guide(buffer_id, 1, 9, 0),
14889            indent_guide(buffer_id, 6, 6, 1),
14890            indent_guide(buffer_id, 8, 8, 1),
14891        ],
14892        None,
14893        &mut cx,
14894    );
14895}
14896
14897#[gpui::test]
14898async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14899    let (buffer_id, mut cx) = setup_indent_guides_editor(
14900        &"
14901        block1
14902            block2
14903                block3
14904                    block4
14905            block2
14906        block1
14907        block1"
14908            .unindent(),
14909        cx,
14910    )
14911    .await;
14912
14913    assert_indent_guides(
14914        1..10,
14915        vec![
14916            indent_guide(buffer_id, 1, 4, 0),
14917            indent_guide(buffer_id, 2, 3, 1),
14918            indent_guide(buffer_id, 3, 3, 2),
14919        ],
14920        None,
14921        &mut cx,
14922    );
14923}
14924
14925#[gpui::test]
14926async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14927    let (buffer_id, mut cx) = setup_indent_guides_editor(
14928        &"
14929        block1
14930            block2
14931                block3
14932
14933        block1
14934        block1"
14935            .unindent(),
14936        cx,
14937    )
14938    .await;
14939
14940    assert_indent_guides(
14941        0..6,
14942        vec![
14943            indent_guide(buffer_id, 1, 2, 0),
14944            indent_guide(buffer_id, 2, 2, 1),
14945        ],
14946        None,
14947        &mut cx,
14948    );
14949}
14950
14951#[gpui::test]
14952async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14953    let (buffer_id, mut cx) = setup_indent_guides_editor(
14954        &"
14955        block1
14956
14957
14958
14959            block2
14960        "
14961        .unindent(),
14962        cx,
14963    )
14964    .await;
14965
14966    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14967}
14968
14969#[gpui::test]
14970async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14971    let (buffer_id, mut cx) = setup_indent_guides_editor(
14972        &"
14973        def a:
14974        \tb = 3
14975        \tif True:
14976        \t\tc = 4
14977        \t\td = 5
14978        \tprint(b)
14979        "
14980        .unindent(),
14981        cx,
14982    )
14983    .await;
14984
14985    assert_indent_guides(
14986        0..6,
14987        vec![
14988            indent_guide(buffer_id, 1, 6, 0),
14989            indent_guide(buffer_id, 3, 4, 1),
14990        ],
14991        None,
14992        &mut cx,
14993    );
14994}
14995
14996#[gpui::test]
14997async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14998    let (buffer_id, mut cx) = setup_indent_guides_editor(
14999        &"
15000    fn main() {
15001        let a = 1;
15002    }"
15003        .unindent(),
15004        cx,
15005    )
15006    .await;
15007
15008    cx.update_editor(|editor, window, cx| {
15009        editor.change_selections(None, window, cx, |s| {
15010            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15011        });
15012    });
15013
15014    assert_indent_guides(
15015        0..3,
15016        vec![indent_guide(buffer_id, 1, 1, 0)],
15017        Some(vec![0]),
15018        &mut cx,
15019    );
15020}
15021
15022#[gpui::test]
15023async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15024    let (buffer_id, mut cx) = setup_indent_guides_editor(
15025        &"
15026    fn main() {
15027        if 1 == 2 {
15028            let a = 1;
15029        }
15030    }"
15031        .unindent(),
15032        cx,
15033    )
15034    .await;
15035
15036    cx.update_editor(|editor, window, cx| {
15037        editor.change_selections(None, window, cx, |s| {
15038            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15039        });
15040    });
15041
15042    assert_indent_guides(
15043        0..4,
15044        vec![
15045            indent_guide(buffer_id, 1, 3, 0),
15046            indent_guide(buffer_id, 2, 2, 1),
15047        ],
15048        Some(vec![1]),
15049        &mut cx,
15050    );
15051
15052    cx.update_editor(|editor, window, cx| {
15053        editor.change_selections(None, window, cx, |s| {
15054            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15055        });
15056    });
15057
15058    assert_indent_guides(
15059        0..4,
15060        vec![
15061            indent_guide(buffer_id, 1, 3, 0),
15062            indent_guide(buffer_id, 2, 2, 1),
15063        ],
15064        Some(vec![1]),
15065        &mut cx,
15066    );
15067
15068    cx.update_editor(|editor, window, cx| {
15069        editor.change_selections(None, window, cx, |s| {
15070            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15071        });
15072    });
15073
15074    assert_indent_guides(
15075        0..4,
15076        vec![
15077            indent_guide(buffer_id, 1, 3, 0),
15078            indent_guide(buffer_id, 2, 2, 1),
15079        ],
15080        Some(vec![0]),
15081        &mut cx,
15082    );
15083}
15084
15085#[gpui::test]
15086async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15087    let (buffer_id, mut cx) = setup_indent_guides_editor(
15088        &"
15089    fn main() {
15090        let a = 1;
15091
15092        let b = 2;
15093    }"
15094        .unindent(),
15095        cx,
15096    )
15097    .await;
15098
15099    cx.update_editor(|editor, window, cx| {
15100        editor.change_selections(None, window, cx, |s| {
15101            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15102        });
15103    });
15104
15105    assert_indent_guides(
15106        0..5,
15107        vec![indent_guide(buffer_id, 1, 3, 0)],
15108        Some(vec![0]),
15109        &mut cx,
15110    );
15111}
15112
15113#[gpui::test]
15114async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15115    let (buffer_id, mut cx) = setup_indent_guides_editor(
15116        &"
15117    def m:
15118        a = 1
15119        pass"
15120            .unindent(),
15121        cx,
15122    )
15123    .await;
15124
15125    cx.update_editor(|editor, window, cx| {
15126        editor.change_selections(None, window, cx, |s| {
15127            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15128        });
15129    });
15130
15131    assert_indent_guides(
15132        0..3,
15133        vec![indent_guide(buffer_id, 1, 2, 0)],
15134        Some(vec![0]),
15135        &mut cx,
15136    );
15137}
15138
15139#[gpui::test]
15140async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15141    init_test(cx, |_| {});
15142    let mut cx = EditorTestContext::new(cx).await;
15143    let text = indoc! {
15144        "
15145        impl A {
15146            fn b() {
15147                0;
15148                3;
15149                5;
15150                6;
15151                7;
15152            }
15153        }
15154        "
15155    };
15156    let base_text = indoc! {
15157        "
15158        impl A {
15159            fn b() {
15160                0;
15161                1;
15162                2;
15163                3;
15164                4;
15165            }
15166            fn c() {
15167                5;
15168                6;
15169                7;
15170            }
15171        }
15172        "
15173    };
15174
15175    cx.update_editor(|editor, window, cx| {
15176        editor.set_text(text, window, cx);
15177
15178        editor.buffer().update(cx, |multibuffer, cx| {
15179            let buffer = multibuffer.as_singleton().unwrap();
15180            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15181
15182            multibuffer.set_all_diff_hunks_expanded(cx);
15183            multibuffer.add_diff(diff, cx);
15184
15185            buffer.read(cx).remote_id()
15186        })
15187    });
15188    cx.run_until_parked();
15189
15190    cx.assert_state_with_diff(
15191        indoc! { "
15192          impl A {
15193              fn b() {
15194                  0;
15195        -         1;
15196        -         2;
15197                  3;
15198        -         4;
15199        -     }
15200        -     fn c() {
15201                  5;
15202                  6;
15203                  7;
15204              }
15205          }
15206          ˇ"
15207        }
15208        .to_string(),
15209    );
15210
15211    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15212        editor
15213            .snapshot(window, cx)
15214            .buffer_snapshot
15215            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15216            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15217            .collect::<Vec<_>>()
15218    });
15219    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15220    assert_eq!(
15221        actual_guides,
15222        vec![
15223            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15224            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15225            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15226        ]
15227    );
15228}
15229
15230#[gpui::test]
15231async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15232    init_test(cx, |_| {});
15233    let mut cx = EditorTestContext::new(cx).await;
15234
15235    let diff_base = r#"
15236        a
15237        b
15238        c
15239        "#
15240    .unindent();
15241
15242    cx.set_state(
15243        &r#"
15244        ˇA
15245        b
15246        C
15247        "#
15248        .unindent(),
15249    );
15250    cx.set_head_text(&diff_base);
15251    cx.update_editor(|editor, window, cx| {
15252        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15253    });
15254    executor.run_until_parked();
15255
15256    let both_hunks_expanded = r#"
15257        - a
15258        + ˇA
15259          b
15260        - c
15261        + C
15262        "#
15263    .unindent();
15264
15265    cx.assert_state_with_diff(both_hunks_expanded.clone());
15266
15267    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15268        let snapshot = editor.snapshot(window, cx);
15269        let hunks = editor
15270            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15271            .collect::<Vec<_>>();
15272        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15273        let buffer_id = hunks[0].buffer_id;
15274        hunks
15275            .into_iter()
15276            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15277            .collect::<Vec<_>>()
15278    });
15279    assert_eq!(hunk_ranges.len(), 2);
15280
15281    cx.update_editor(|editor, _, cx| {
15282        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15283    });
15284    executor.run_until_parked();
15285
15286    let second_hunk_expanded = r#"
15287          ˇA
15288          b
15289        - c
15290        + C
15291        "#
15292    .unindent();
15293
15294    cx.assert_state_with_diff(second_hunk_expanded);
15295
15296    cx.update_editor(|editor, _, cx| {
15297        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15298    });
15299    executor.run_until_parked();
15300
15301    cx.assert_state_with_diff(both_hunks_expanded.clone());
15302
15303    cx.update_editor(|editor, _, cx| {
15304        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15305    });
15306    executor.run_until_parked();
15307
15308    let first_hunk_expanded = r#"
15309        - a
15310        + ˇA
15311          b
15312          C
15313        "#
15314    .unindent();
15315
15316    cx.assert_state_with_diff(first_hunk_expanded);
15317
15318    cx.update_editor(|editor, _, cx| {
15319        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15320    });
15321    executor.run_until_parked();
15322
15323    cx.assert_state_with_diff(both_hunks_expanded);
15324
15325    cx.set_state(
15326        &r#"
15327        ˇA
15328        b
15329        "#
15330        .unindent(),
15331    );
15332    cx.run_until_parked();
15333
15334    // TODO this cursor position seems bad
15335    cx.assert_state_with_diff(
15336        r#"
15337        - ˇa
15338        + A
15339          b
15340        "#
15341        .unindent(),
15342    );
15343
15344    cx.update_editor(|editor, window, cx| {
15345        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15346    });
15347
15348    cx.assert_state_with_diff(
15349        r#"
15350            - ˇa
15351            + A
15352              b
15353            - c
15354            "#
15355        .unindent(),
15356    );
15357
15358    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15359        let snapshot = editor.snapshot(window, cx);
15360        let hunks = editor
15361            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15362            .collect::<Vec<_>>();
15363        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15364        let buffer_id = hunks[0].buffer_id;
15365        hunks
15366            .into_iter()
15367            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15368            .collect::<Vec<_>>()
15369    });
15370    assert_eq!(hunk_ranges.len(), 2);
15371
15372    cx.update_editor(|editor, _, cx| {
15373        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15374    });
15375    executor.run_until_parked();
15376
15377    cx.assert_state_with_diff(
15378        r#"
15379        - ˇa
15380        + A
15381          b
15382        "#
15383        .unindent(),
15384    );
15385}
15386
15387#[gpui::test]
15388async fn test_toggle_deletion_hunk_at_start_of_file(
15389    executor: BackgroundExecutor,
15390    cx: &mut TestAppContext,
15391) {
15392    init_test(cx, |_| {});
15393    let mut cx = EditorTestContext::new(cx).await;
15394
15395    let diff_base = r#"
15396        a
15397        b
15398        c
15399        "#
15400    .unindent();
15401
15402    cx.set_state(
15403        &r#"
15404        ˇb
15405        c
15406        "#
15407        .unindent(),
15408    );
15409    cx.set_head_text(&diff_base);
15410    cx.update_editor(|editor, window, cx| {
15411        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15412    });
15413    executor.run_until_parked();
15414
15415    let hunk_expanded = r#"
15416        - a
15417          ˇb
15418          c
15419        "#
15420    .unindent();
15421
15422    cx.assert_state_with_diff(hunk_expanded.clone());
15423
15424    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15425        let snapshot = editor.snapshot(window, cx);
15426        let hunks = editor
15427            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15428            .collect::<Vec<_>>();
15429        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15430        let buffer_id = hunks[0].buffer_id;
15431        hunks
15432            .into_iter()
15433            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15434            .collect::<Vec<_>>()
15435    });
15436    assert_eq!(hunk_ranges.len(), 1);
15437
15438    cx.update_editor(|editor, _, cx| {
15439        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15440    });
15441    executor.run_until_parked();
15442
15443    let hunk_collapsed = r#"
15444          ˇb
15445          c
15446        "#
15447    .unindent();
15448
15449    cx.assert_state_with_diff(hunk_collapsed);
15450
15451    cx.update_editor(|editor, _, cx| {
15452        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15453    });
15454    executor.run_until_parked();
15455
15456    cx.assert_state_with_diff(hunk_expanded.clone());
15457}
15458
15459#[gpui::test]
15460async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15461    init_test(cx, |_| {});
15462
15463    let fs = FakeFs::new(cx.executor());
15464    fs.insert_tree(
15465        path!("/test"),
15466        json!({
15467            ".git": {},
15468            "file-1": "ONE\n",
15469            "file-2": "TWO\n",
15470            "file-3": "THREE\n",
15471        }),
15472    )
15473    .await;
15474
15475    fs.set_head_for_repo(
15476        path!("/test/.git").as_ref(),
15477        &[
15478            ("file-1".into(), "one\n".into()),
15479            ("file-2".into(), "two\n".into()),
15480            ("file-3".into(), "three\n".into()),
15481        ],
15482    );
15483
15484    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15485    let mut buffers = vec![];
15486    for i in 1..=3 {
15487        let buffer = project
15488            .update(cx, |project, cx| {
15489                let path = format!(path!("/test/file-{}"), i);
15490                project.open_local_buffer(path, cx)
15491            })
15492            .await
15493            .unwrap();
15494        buffers.push(buffer);
15495    }
15496
15497    let multibuffer = cx.new(|cx| {
15498        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15499        multibuffer.set_all_diff_hunks_expanded(cx);
15500        for buffer in &buffers {
15501            let snapshot = buffer.read(cx).snapshot();
15502            multibuffer.set_excerpts_for_path(
15503                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15504                buffer.clone(),
15505                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15506                DEFAULT_MULTIBUFFER_CONTEXT,
15507                cx,
15508            );
15509        }
15510        multibuffer
15511    });
15512
15513    let editor = cx.add_window(|window, cx| {
15514        Editor::new(
15515            EditorMode::Full,
15516            multibuffer,
15517            Some(project),
15518            true,
15519            window,
15520            cx,
15521        )
15522    });
15523    cx.run_until_parked();
15524
15525    let snapshot = editor
15526        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15527        .unwrap();
15528    let hunks = snapshot
15529        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15530        .map(|hunk| match hunk {
15531            DisplayDiffHunk::Unfolded {
15532                display_row_range, ..
15533            } => display_row_range,
15534            DisplayDiffHunk::Folded { .. } => unreachable!(),
15535        })
15536        .collect::<Vec<_>>();
15537    assert_eq!(
15538        hunks,
15539        [
15540            DisplayRow(3)..DisplayRow(5),
15541            DisplayRow(10)..DisplayRow(12),
15542            DisplayRow(17)..DisplayRow(19),
15543        ]
15544    );
15545}
15546
15547#[gpui::test]
15548async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15549    init_test(cx, |_| {});
15550
15551    let mut cx = EditorTestContext::new(cx).await;
15552    cx.set_head_text(indoc! { "
15553        one
15554        two
15555        three
15556        four
15557        five
15558        "
15559    });
15560    cx.set_index_text(indoc! { "
15561        one
15562        two
15563        three
15564        four
15565        five
15566        "
15567    });
15568    cx.set_state(indoc! {"
15569        one
15570        TWO
15571        ˇTHREE
15572        FOUR
15573        five
15574    "});
15575    cx.run_until_parked();
15576    cx.update_editor(|editor, window, cx| {
15577        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15578    });
15579    cx.run_until_parked();
15580    cx.assert_index_text(Some(indoc! {"
15581        one
15582        TWO
15583        THREE
15584        FOUR
15585        five
15586    "}));
15587    cx.set_state(indoc! { "
15588        one
15589        TWO
15590        ˇTHREE-HUNDRED
15591        FOUR
15592        five
15593    "});
15594    cx.run_until_parked();
15595    cx.update_editor(|editor, window, cx| {
15596        let snapshot = editor.snapshot(window, cx);
15597        let hunks = editor
15598            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15599            .collect::<Vec<_>>();
15600        assert_eq!(hunks.len(), 1);
15601        assert_eq!(
15602            hunks[0].status(),
15603            DiffHunkStatus {
15604                kind: DiffHunkStatusKind::Modified,
15605                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15606            }
15607        );
15608
15609        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15610    });
15611    cx.run_until_parked();
15612    cx.assert_index_text(Some(indoc! {"
15613        one
15614        TWO
15615        THREE-HUNDRED
15616        FOUR
15617        five
15618    "}));
15619}
15620
15621#[gpui::test]
15622fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15623    init_test(cx, |_| {});
15624
15625    let editor = cx.add_window(|window, cx| {
15626        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15627        build_editor(buffer, window, cx)
15628    });
15629
15630    let render_args = Arc::new(Mutex::new(None));
15631    let snapshot = editor
15632        .update(cx, |editor, window, cx| {
15633            let snapshot = editor.buffer().read(cx).snapshot(cx);
15634            let range =
15635                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15636
15637            struct RenderArgs {
15638                row: MultiBufferRow,
15639                folded: bool,
15640                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15641            }
15642
15643            let crease = Crease::inline(
15644                range,
15645                FoldPlaceholder::test(),
15646                {
15647                    let toggle_callback = render_args.clone();
15648                    move |row, folded, callback, _window, _cx| {
15649                        *toggle_callback.lock() = Some(RenderArgs {
15650                            row,
15651                            folded,
15652                            callback,
15653                        });
15654                        div()
15655                    }
15656                },
15657                |_row, _folded, _window, _cx| div(),
15658            );
15659
15660            editor.insert_creases(Some(crease), cx);
15661            let snapshot = editor.snapshot(window, cx);
15662            let _div = snapshot.render_crease_toggle(
15663                MultiBufferRow(1),
15664                false,
15665                cx.entity().clone(),
15666                window,
15667                cx,
15668            );
15669            snapshot
15670        })
15671        .unwrap();
15672
15673    let render_args = render_args.lock().take().unwrap();
15674    assert_eq!(render_args.row, MultiBufferRow(1));
15675    assert!(!render_args.folded);
15676    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15677
15678    cx.update_window(*editor, |_, window, cx| {
15679        (render_args.callback)(true, window, cx)
15680    })
15681    .unwrap();
15682    let snapshot = editor
15683        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15684        .unwrap();
15685    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15686
15687    cx.update_window(*editor, |_, window, cx| {
15688        (render_args.callback)(false, window, cx)
15689    })
15690    .unwrap();
15691    let snapshot = editor
15692        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15693        .unwrap();
15694    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15695}
15696
15697#[gpui::test]
15698async fn test_input_text(cx: &mut TestAppContext) {
15699    init_test(cx, |_| {});
15700    let mut cx = EditorTestContext::new(cx).await;
15701
15702    cx.set_state(
15703        &r#"ˇone
15704        two
15705
15706        three
15707        fourˇ
15708        five
15709
15710        siˇx"#
15711            .unindent(),
15712    );
15713
15714    cx.dispatch_action(HandleInput(String::new()));
15715    cx.assert_editor_state(
15716        &r#"ˇone
15717        two
15718
15719        three
15720        fourˇ
15721        five
15722
15723        siˇx"#
15724            .unindent(),
15725    );
15726
15727    cx.dispatch_action(HandleInput("AAAA".to_string()));
15728    cx.assert_editor_state(
15729        &r#"AAAAˇone
15730        two
15731
15732        three
15733        fourAAAAˇ
15734        five
15735
15736        siAAAAˇx"#
15737            .unindent(),
15738    );
15739}
15740
15741#[gpui::test]
15742async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15743    init_test(cx, |_| {});
15744
15745    let mut cx = EditorTestContext::new(cx).await;
15746    cx.set_state(
15747        r#"let foo = 1;
15748let foo = 2;
15749let foo = 3;
15750let fooˇ = 4;
15751let foo = 5;
15752let foo = 6;
15753let foo = 7;
15754let foo = 8;
15755let foo = 9;
15756let foo = 10;
15757let foo = 11;
15758let foo = 12;
15759let foo = 13;
15760let foo = 14;
15761let foo = 15;"#,
15762    );
15763
15764    cx.update_editor(|e, window, cx| {
15765        assert_eq!(
15766            e.next_scroll_position,
15767            NextScrollCursorCenterTopBottom::Center,
15768            "Default next scroll direction is center",
15769        );
15770
15771        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15772        assert_eq!(
15773            e.next_scroll_position,
15774            NextScrollCursorCenterTopBottom::Top,
15775            "After center, next scroll direction should be top",
15776        );
15777
15778        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15779        assert_eq!(
15780            e.next_scroll_position,
15781            NextScrollCursorCenterTopBottom::Bottom,
15782            "After top, next scroll direction should be bottom",
15783        );
15784
15785        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15786        assert_eq!(
15787            e.next_scroll_position,
15788            NextScrollCursorCenterTopBottom::Center,
15789            "After bottom, scrolling should start over",
15790        );
15791
15792        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15793        assert_eq!(
15794            e.next_scroll_position,
15795            NextScrollCursorCenterTopBottom::Top,
15796            "Scrolling continues if retriggered fast enough"
15797        );
15798    });
15799
15800    cx.executor()
15801        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15802    cx.executor().run_until_parked();
15803    cx.update_editor(|e, _, _| {
15804        assert_eq!(
15805            e.next_scroll_position,
15806            NextScrollCursorCenterTopBottom::Center,
15807            "If scrolling is not triggered fast enough, it should reset"
15808        );
15809    });
15810}
15811
15812#[gpui::test]
15813async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15814    init_test(cx, |_| {});
15815    let mut cx = EditorLspTestContext::new_rust(
15816        lsp::ServerCapabilities {
15817            definition_provider: Some(lsp::OneOf::Left(true)),
15818            references_provider: Some(lsp::OneOf::Left(true)),
15819            ..lsp::ServerCapabilities::default()
15820        },
15821        cx,
15822    )
15823    .await;
15824
15825    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15826        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15827            move |params, _| async move {
15828                if empty_go_to_definition {
15829                    Ok(None)
15830                } else {
15831                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15832                        uri: params.text_document_position_params.text_document.uri,
15833                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15834                    })))
15835                }
15836            },
15837        );
15838        let references =
15839            cx.lsp
15840                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15841                    Ok(Some(vec![lsp::Location {
15842                        uri: params.text_document_position.text_document.uri,
15843                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15844                    }]))
15845                });
15846        (go_to_definition, references)
15847    };
15848
15849    cx.set_state(
15850        &r#"fn one() {
15851            let mut a = ˇtwo();
15852        }
15853
15854        fn two() {}"#
15855            .unindent(),
15856    );
15857    set_up_lsp_handlers(false, &mut cx);
15858    let navigated = cx
15859        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15860        .await
15861        .expect("Failed to navigate to definition");
15862    assert_eq!(
15863        navigated,
15864        Navigated::Yes,
15865        "Should have navigated to definition from the GetDefinition response"
15866    );
15867    cx.assert_editor_state(
15868        &r#"fn one() {
15869            let mut a = two();
15870        }
15871
15872        fn «twoˇ»() {}"#
15873            .unindent(),
15874    );
15875
15876    let editors = cx.update_workspace(|workspace, _, cx| {
15877        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15878    });
15879    cx.update_editor(|_, _, test_editor_cx| {
15880        assert_eq!(
15881            editors.len(),
15882            1,
15883            "Initially, only one, test, editor should be open in the workspace"
15884        );
15885        assert_eq!(
15886            test_editor_cx.entity(),
15887            editors.last().expect("Asserted len is 1").clone()
15888        );
15889    });
15890
15891    set_up_lsp_handlers(true, &mut cx);
15892    let navigated = cx
15893        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15894        .await
15895        .expect("Failed to navigate to lookup references");
15896    assert_eq!(
15897        navigated,
15898        Navigated::Yes,
15899        "Should have navigated to references as a fallback after empty GoToDefinition response"
15900    );
15901    // We should not change the selections in the existing file,
15902    // if opening another milti buffer with the references
15903    cx.assert_editor_state(
15904        &r#"fn one() {
15905            let mut a = two();
15906        }
15907
15908        fn «twoˇ»() {}"#
15909            .unindent(),
15910    );
15911    let editors = cx.update_workspace(|workspace, _, cx| {
15912        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15913    });
15914    cx.update_editor(|_, _, test_editor_cx| {
15915        assert_eq!(
15916            editors.len(),
15917            2,
15918            "After falling back to references search, we open a new editor with the results"
15919        );
15920        let references_fallback_text = editors
15921            .into_iter()
15922            .find(|new_editor| *new_editor != test_editor_cx.entity())
15923            .expect("Should have one non-test editor now")
15924            .read(test_editor_cx)
15925            .text(test_editor_cx);
15926        assert_eq!(
15927            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15928            "Should use the range from the references response and not the GoToDefinition one"
15929        );
15930    });
15931}
15932
15933#[gpui::test]
15934async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15935    init_test(cx, |_| {});
15936
15937    let language = Arc::new(Language::new(
15938        LanguageConfig::default(),
15939        Some(tree_sitter_rust::LANGUAGE.into()),
15940    ));
15941
15942    let text = r#"
15943        #[cfg(test)]
15944        mod tests() {
15945            #[test]
15946            fn runnable_1() {
15947                let a = 1;
15948            }
15949
15950            #[test]
15951            fn runnable_2() {
15952                let a = 1;
15953                let b = 2;
15954            }
15955        }
15956    "#
15957    .unindent();
15958
15959    let fs = FakeFs::new(cx.executor());
15960    fs.insert_file("/file.rs", Default::default()).await;
15961
15962    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15963    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15964    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15965    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15966    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15967
15968    let editor = cx.new_window_entity(|window, cx| {
15969        Editor::new(
15970            EditorMode::Full,
15971            multi_buffer,
15972            Some(project.clone()),
15973            true,
15974            window,
15975            cx,
15976        )
15977    });
15978
15979    editor.update_in(cx, |editor, window, cx| {
15980        let snapshot = editor.buffer().read(cx).snapshot(cx);
15981        editor.tasks.insert(
15982            (buffer.read(cx).remote_id(), 3),
15983            RunnableTasks {
15984                templates: vec![],
15985                offset: snapshot.anchor_before(43),
15986                column: 0,
15987                extra_variables: HashMap::default(),
15988                context_range: BufferOffset(43)..BufferOffset(85),
15989            },
15990        );
15991        editor.tasks.insert(
15992            (buffer.read(cx).remote_id(), 8),
15993            RunnableTasks {
15994                templates: vec![],
15995                offset: snapshot.anchor_before(86),
15996                column: 0,
15997                extra_variables: HashMap::default(),
15998                context_range: BufferOffset(86)..BufferOffset(191),
15999            },
16000        );
16001
16002        // Test finding task when cursor is inside function body
16003        editor.change_selections(None, window, cx, |s| {
16004            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16005        });
16006        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16007        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16008
16009        // Test finding task when cursor is on function name
16010        editor.change_selections(None, window, cx, |s| {
16011            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16012        });
16013        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16014        assert_eq!(row, 8, "Should find task when cursor is on function name");
16015    });
16016}
16017
16018#[gpui::test]
16019async fn test_folding_buffers(cx: &mut TestAppContext) {
16020    init_test(cx, |_| {});
16021
16022    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16023    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16024    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16025
16026    let fs = FakeFs::new(cx.executor());
16027    fs.insert_tree(
16028        path!("/a"),
16029        json!({
16030            "first.rs": sample_text_1,
16031            "second.rs": sample_text_2,
16032            "third.rs": sample_text_3,
16033        }),
16034    )
16035    .await;
16036    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16037    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16038    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16039    let worktree = project.update(cx, |project, cx| {
16040        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16041        assert_eq!(worktrees.len(), 1);
16042        worktrees.pop().unwrap()
16043    });
16044    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16045
16046    let buffer_1 = project
16047        .update(cx, |project, cx| {
16048            project.open_buffer((worktree_id, "first.rs"), cx)
16049        })
16050        .await
16051        .unwrap();
16052    let buffer_2 = project
16053        .update(cx, |project, cx| {
16054            project.open_buffer((worktree_id, "second.rs"), cx)
16055        })
16056        .await
16057        .unwrap();
16058    let buffer_3 = project
16059        .update(cx, |project, cx| {
16060            project.open_buffer((worktree_id, "third.rs"), cx)
16061        })
16062        .await
16063        .unwrap();
16064
16065    let multi_buffer = cx.new(|cx| {
16066        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16067        multi_buffer.push_excerpts(
16068            buffer_1.clone(),
16069            [
16070                ExcerptRange {
16071                    context: Point::new(0, 0)..Point::new(3, 0),
16072                    primary: None,
16073                },
16074                ExcerptRange {
16075                    context: Point::new(5, 0)..Point::new(7, 0),
16076                    primary: None,
16077                },
16078                ExcerptRange {
16079                    context: Point::new(9, 0)..Point::new(10, 4),
16080                    primary: None,
16081                },
16082            ],
16083            cx,
16084        );
16085        multi_buffer.push_excerpts(
16086            buffer_2.clone(),
16087            [
16088                ExcerptRange {
16089                    context: Point::new(0, 0)..Point::new(3, 0),
16090                    primary: None,
16091                },
16092                ExcerptRange {
16093                    context: Point::new(5, 0)..Point::new(7, 0),
16094                    primary: None,
16095                },
16096                ExcerptRange {
16097                    context: Point::new(9, 0)..Point::new(10, 4),
16098                    primary: None,
16099                },
16100            ],
16101            cx,
16102        );
16103        multi_buffer.push_excerpts(
16104            buffer_3.clone(),
16105            [
16106                ExcerptRange {
16107                    context: Point::new(0, 0)..Point::new(3, 0),
16108                    primary: None,
16109                },
16110                ExcerptRange {
16111                    context: Point::new(5, 0)..Point::new(7, 0),
16112                    primary: None,
16113                },
16114                ExcerptRange {
16115                    context: Point::new(9, 0)..Point::new(10, 4),
16116                    primary: None,
16117                },
16118            ],
16119            cx,
16120        );
16121        multi_buffer
16122    });
16123    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16124        Editor::new(
16125            EditorMode::Full,
16126            multi_buffer.clone(),
16127            Some(project.clone()),
16128            true,
16129            window,
16130            cx,
16131        )
16132    });
16133
16134    assert_eq!(
16135        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16136        "\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",
16137    );
16138
16139    multi_buffer_editor.update(cx, |editor, cx| {
16140        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16141    });
16142    assert_eq!(
16143        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16144        "\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",
16145        "After folding the first buffer, its text should not be displayed"
16146    );
16147
16148    multi_buffer_editor.update(cx, |editor, cx| {
16149        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16150    });
16151    assert_eq!(
16152        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16153        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
16154        "After folding the second buffer, its text should not be displayed"
16155    );
16156
16157    multi_buffer_editor.update(cx, |editor, cx| {
16158        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16159    });
16160    assert_eq!(
16161        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16162        "\n\n\n\n\n",
16163        "After folding the third buffer, its text should not be displayed"
16164    );
16165
16166    // Emulate selection inside the fold logic, that should work
16167    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16168        editor
16169            .snapshot(window, cx)
16170            .next_line_boundary(Point::new(0, 4));
16171    });
16172
16173    multi_buffer_editor.update(cx, |editor, cx| {
16174        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16175    });
16176    assert_eq!(
16177        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16178        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
16179        "After unfolding the second buffer, its text should be displayed"
16180    );
16181
16182    // Typing inside of buffer 1 causes that buffer to be unfolded.
16183    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16184        assert_eq!(
16185            multi_buffer
16186                .read(cx)
16187                .snapshot(cx)
16188                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16189                .collect::<String>(),
16190            "bbbb"
16191        );
16192        editor.change_selections(None, window, cx, |selections| {
16193            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16194        });
16195        editor.handle_input("B", window, cx);
16196    });
16197
16198    assert_eq!(
16199        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16200        "\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",
16201        "After unfolding the first buffer, its and 2nd buffer's 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        "\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",
16210        "After unfolding the all buffers, all original text should be displayed"
16211    );
16212}
16213
16214#[gpui::test]
16215async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16216    init_test(cx, |_| {});
16217
16218    let sample_text_1 = "1111\n2222\n3333".to_string();
16219    let sample_text_2 = "4444\n5555\n6666".to_string();
16220    let sample_text_3 = "7777\n8888\n9999".to_string();
16221
16222    let fs = FakeFs::new(cx.executor());
16223    fs.insert_tree(
16224        path!("/a"),
16225        json!({
16226            "first.rs": sample_text_1,
16227            "second.rs": sample_text_2,
16228            "third.rs": sample_text_3,
16229        }),
16230    )
16231    .await;
16232    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16233    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16234    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16235    let worktree = project.update(cx, |project, cx| {
16236        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16237        assert_eq!(worktrees.len(), 1);
16238        worktrees.pop().unwrap()
16239    });
16240    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16241
16242    let buffer_1 = project
16243        .update(cx, |project, cx| {
16244            project.open_buffer((worktree_id, "first.rs"), cx)
16245        })
16246        .await
16247        .unwrap();
16248    let buffer_2 = project
16249        .update(cx, |project, cx| {
16250            project.open_buffer((worktree_id, "second.rs"), cx)
16251        })
16252        .await
16253        .unwrap();
16254    let buffer_3 = project
16255        .update(cx, |project, cx| {
16256            project.open_buffer((worktree_id, "third.rs"), cx)
16257        })
16258        .await
16259        .unwrap();
16260
16261    let multi_buffer = cx.new(|cx| {
16262        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16263        multi_buffer.push_excerpts(
16264            buffer_1.clone(),
16265            [ExcerptRange {
16266                context: Point::new(0, 0)..Point::new(3, 0),
16267                primary: None,
16268            }],
16269            cx,
16270        );
16271        multi_buffer.push_excerpts(
16272            buffer_2.clone(),
16273            [ExcerptRange {
16274                context: Point::new(0, 0)..Point::new(3, 0),
16275                primary: None,
16276            }],
16277            cx,
16278        );
16279        multi_buffer.push_excerpts(
16280            buffer_3.clone(),
16281            [ExcerptRange {
16282                context: Point::new(0, 0)..Point::new(3, 0),
16283                primary: None,
16284            }],
16285            cx,
16286        );
16287        multi_buffer
16288    });
16289
16290    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16291        Editor::new(
16292            EditorMode::Full,
16293            multi_buffer,
16294            Some(project.clone()),
16295            true,
16296            window,
16297            cx,
16298        )
16299    });
16300
16301    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
16302    assert_eq!(
16303        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16304        full_text,
16305    );
16306
16307    multi_buffer_editor.update(cx, |editor, cx| {
16308        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16309    });
16310    assert_eq!(
16311        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16312        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
16313        "After folding the first buffer, its text should not be displayed"
16314    );
16315
16316    multi_buffer_editor.update(cx, |editor, cx| {
16317        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16318    });
16319
16320    assert_eq!(
16321        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16322        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
16323        "After folding the second buffer, its text should not be displayed"
16324    );
16325
16326    multi_buffer_editor.update(cx, |editor, cx| {
16327        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16328    });
16329    assert_eq!(
16330        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16331        "\n\n\n\n\n",
16332        "After folding the third buffer, its text should not be displayed"
16333    );
16334
16335    multi_buffer_editor.update(cx, |editor, cx| {
16336        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16337    });
16338    assert_eq!(
16339        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16340        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
16341        "After unfolding the second buffer, its text should be displayed"
16342    );
16343
16344    multi_buffer_editor.update(cx, |editor, cx| {
16345        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16346    });
16347    assert_eq!(
16348        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16349        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
16350        "After unfolding the first buffer, its text should be displayed"
16351    );
16352
16353    multi_buffer_editor.update(cx, |editor, cx| {
16354        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16355    });
16356    assert_eq!(
16357        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16358        full_text,
16359        "After unfolding all buffers, all original text should be displayed"
16360    );
16361}
16362
16363#[gpui::test]
16364async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16365    init_test(cx, |_| {});
16366
16367    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16368
16369    let fs = FakeFs::new(cx.executor());
16370    fs.insert_tree(
16371        path!("/a"),
16372        json!({
16373            "main.rs": sample_text,
16374        }),
16375    )
16376    .await;
16377    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16378    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16379    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16380    let worktree = project.update(cx, |project, cx| {
16381        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16382        assert_eq!(worktrees.len(), 1);
16383        worktrees.pop().unwrap()
16384    });
16385    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16386
16387    let buffer_1 = project
16388        .update(cx, |project, cx| {
16389            project.open_buffer((worktree_id, "main.rs"), cx)
16390        })
16391        .await
16392        .unwrap();
16393
16394    let multi_buffer = cx.new(|cx| {
16395        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16396        multi_buffer.push_excerpts(
16397            buffer_1.clone(),
16398            [ExcerptRange {
16399                context: Point::new(0, 0)
16400                    ..Point::new(
16401                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16402                        0,
16403                    ),
16404                primary: None,
16405            }],
16406            cx,
16407        );
16408        multi_buffer
16409    });
16410    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16411        Editor::new(
16412            EditorMode::Full,
16413            multi_buffer,
16414            Some(project.clone()),
16415            true,
16416            window,
16417            cx,
16418        )
16419    });
16420
16421    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16422    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16423        enum TestHighlight {}
16424        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16425        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16426        editor.highlight_text::<TestHighlight>(
16427            vec![highlight_range.clone()],
16428            HighlightStyle::color(Hsla::green()),
16429            cx,
16430        );
16431        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16432    });
16433
16434    let full_text = format!("\n\n\n{sample_text}\n");
16435    assert_eq!(
16436        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16437        full_text,
16438    );
16439}
16440
16441#[gpui::test]
16442async fn test_inline_completion_text(cx: &mut TestAppContext) {
16443    init_test(cx, |_| {});
16444
16445    // Simple insertion
16446    assert_highlighted_edits(
16447        "Hello, world!",
16448        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16449        true,
16450        cx,
16451        |highlighted_edits, cx| {
16452            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16453            assert_eq!(highlighted_edits.highlights.len(), 1);
16454            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16455            assert_eq!(
16456                highlighted_edits.highlights[0].1.background_color,
16457                Some(cx.theme().status().created_background)
16458            );
16459        },
16460    )
16461    .await;
16462
16463    // Replacement
16464    assert_highlighted_edits(
16465        "This is a test.",
16466        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16467        false,
16468        cx,
16469        |highlighted_edits, cx| {
16470            assert_eq!(highlighted_edits.text, "That is a test.");
16471            assert_eq!(highlighted_edits.highlights.len(), 1);
16472            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16473            assert_eq!(
16474                highlighted_edits.highlights[0].1.background_color,
16475                Some(cx.theme().status().created_background)
16476            );
16477        },
16478    )
16479    .await;
16480
16481    // Multiple edits
16482    assert_highlighted_edits(
16483        "Hello, world!",
16484        vec![
16485            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16486            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16487        ],
16488        false,
16489        cx,
16490        |highlighted_edits, cx| {
16491            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16492            assert_eq!(highlighted_edits.highlights.len(), 2);
16493            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16494            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16495            assert_eq!(
16496                highlighted_edits.highlights[0].1.background_color,
16497                Some(cx.theme().status().created_background)
16498            );
16499            assert_eq!(
16500                highlighted_edits.highlights[1].1.background_color,
16501                Some(cx.theme().status().created_background)
16502            );
16503        },
16504    )
16505    .await;
16506
16507    // Multiple lines with edits
16508    assert_highlighted_edits(
16509        "First line\nSecond line\nThird line\nFourth line",
16510        vec![
16511            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16512            (
16513                Point::new(2, 0)..Point::new(2, 10),
16514                "New third line".to_string(),
16515            ),
16516            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16517        ],
16518        false,
16519        cx,
16520        |highlighted_edits, cx| {
16521            assert_eq!(
16522                highlighted_edits.text,
16523                "Second modified\nNew third line\nFourth updated line"
16524            );
16525            assert_eq!(highlighted_edits.highlights.len(), 3);
16526            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16527            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16528            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16529            for highlight in &highlighted_edits.highlights {
16530                assert_eq!(
16531                    highlight.1.background_color,
16532                    Some(cx.theme().status().created_background)
16533                );
16534            }
16535        },
16536    )
16537    .await;
16538}
16539
16540#[gpui::test]
16541async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16542    init_test(cx, |_| {});
16543
16544    // Deletion
16545    assert_highlighted_edits(
16546        "Hello, world!",
16547        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16548        true,
16549        cx,
16550        |highlighted_edits, cx| {
16551            assert_eq!(highlighted_edits.text, "Hello, world!");
16552            assert_eq!(highlighted_edits.highlights.len(), 1);
16553            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16554            assert_eq!(
16555                highlighted_edits.highlights[0].1.background_color,
16556                Some(cx.theme().status().deleted_background)
16557            );
16558        },
16559    )
16560    .await;
16561
16562    // Insertion
16563    assert_highlighted_edits(
16564        "Hello, world!",
16565        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16566        true,
16567        cx,
16568        |highlighted_edits, cx| {
16569            assert_eq!(highlighted_edits.highlights.len(), 1);
16570            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16571            assert_eq!(
16572                highlighted_edits.highlights[0].1.background_color,
16573                Some(cx.theme().status().created_background)
16574            );
16575        },
16576    )
16577    .await;
16578}
16579
16580async fn assert_highlighted_edits(
16581    text: &str,
16582    edits: Vec<(Range<Point>, String)>,
16583    include_deletions: bool,
16584    cx: &mut TestAppContext,
16585    assertion_fn: impl Fn(HighlightedText, &App),
16586) {
16587    let window = cx.add_window(|window, cx| {
16588        let buffer = MultiBuffer::build_simple(text, cx);
16589        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16590    });
16591    let cx = &mut VisualTestContext::from_window(*window, cx);
16592
16593    let (buffer, snapshot) = window
16594        .update(cx, |editor, _window, cx| {
16595            (
16596                editor.buffer().clone(),
16597                editor.buffer().read(cx).snapshot(cx),
16598            )
16599        })
16600        .unwrap();
16601
16602    let edits = edits
16603        .into_iter()
16604        .map(|(range, edit)| {
16605            (
16606                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16607                edit,
16608            )
16609        })
16610        .collect::<Vec<_>>();
16611
16612    let text_anchor_edits = edits
16613        .clone()
16614        .into_iter()
16615        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16616        .collect::<Vec<_>>();
16617
16618    let edit_preview = window
16619        .update(cx, |_, _window, cx| {
16620            buffer
16621                .read(cx)
16622                .as_singleton()
16623                .unwrap()
16624                .read(cx)
16625                .preview_edits(text_anchor_edits.into(), cx)
16626        })
16627        .unwrap()
16628        .await;
16629
16630    cx.update(|_window, cx| {
16631        let highlighted_edits = inline_completion_edit_text(
16632            &snapshot.as_singleton().unwrap().2,
16633            &edits,
16634            &edit_preview,
16635            include_deletions,
16636            cx,
16637        );
16638        assertion_fn(highlighted_edits, cx)
16639    });
16640}
16641
16642#[gpui::test]
16643async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16644    init_test(cx, |_| {});
16645    let capabilities = lsp::ServerCapabilities {
16646        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16647            prepare_provider: Some(true),
16648            work_done_progress_options: Default::default(),
16649        })),
16650        ..Default::default()
16651    };
16652    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16653
16654    cx.set_state(indoc! {"
16655        struct Fˇoo {}
16656    "});
16657
16658    cx.update_editor(|editor, _, cx| {
16659        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16660        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16661        editor.highlight_background::<DocumentHighlightRead>(
16662            &[highlight_range],
16663            |c| c.editor_document_highlight_read_background,
16664            cx,
16665        );
16666    });
16667
16668    let mut prepare_rename_handler =
16669        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16670            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16671                start: lsp::Position {
16672                    line: 0,
16673                    character: 7,
16674                },
16675                end: lsp::Position {
16676                    line: 0,
16677                    character: 10,
16678                },
16679            })))
16680        });
16681    let prepare_rename_task = cx
16682        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16683        .expect("Prepare rename was not started");
16684    prepare_rename_handler.next().await.unwrap();
16685    prepare_rename_task.await.expect("Prepare rename failed");
16686
16687    let mut rename_handler =
16688        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16689            let edit = lsp::TextEdit {
16690                range: lsp::Range {
16691                    start: lsp::Position {
16692                        line: 0,
16693                        character: 7,
16694                    },
16695                    end: lsp::Position {
16696                        line: 0,
16697                        character: 10,
16698                    },
16699                },
16700                new_text: "FooRenamed".to_string(),
16701            };
16702            Ok(Some(lsp::WorkspaceEdit::new(
16703                // Specify the same edit twice
16704                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16705            )))
16706        });
16707    let rename_task = cx
16708        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16709        .expect("Confirm rename was not started");
16710    rename_handler.next().await.unwrap();
16711    rename_task.await.expect("Confirm rename failed");
16712    cx.run_until_parked();
16713
16714    // Despite two edits, only one is actually applied as those are identical
16715    cx.assert_editor_state(indoc! {"
16716        struct FooRenamedˇ {}
16717    "});
16718}
16719
16720#[gpui::test]
16721async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16722    init_test(cx, |_| {});
16723    // These capabilities indicate that the server does not support prepare rename.
16724    let capabilities = lsp::ServerCapabilities {
16725        rename_provider: Some(lsp::OneOf::Left(true)),
16726        ..Default::default()
16727    };
16728    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16729
16730    cx.set_state(indoc! {"
16731        struct Fˇoo {}
16732    "});
16733
16734    cx.update_editor(|editor, _window, cx| {
16735        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16736        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16737        editor.highlight_background::<DocumentHighlightRead>(
16738            &[highlight_range],
16739            |c| c.editor_document_highlight_read_background,
16740            cx,
16741        );
16742    });
16743
16744    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16745        .expect("Prepare rename was not started")
16746        .await
16747        .expect("Prepare rename failed");
16748
16749    let mut rename_handler =
16750        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16751            let edit = lsp::TextEdit {
16752                range: lsp::Range {
16753                    start: lsp::Position {
16754                        line: 0,
16755                        character: 7,
16756                    },
16757                    end: lsp::Position {
16758                        line: 0,
16759                        character: 10,
16760                    },
16761                },
16762                new_text: "FooRenamed".to_string(),
16763            };
16764            Ok(Some(lsp::WorkspaceEdit::new(
16765                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16766            )))
16767        });
16768    let rename_task = cx
16769        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16770        .expect("Confirm rename was not started");
16771    rename_handler.next().await.unwrap();
16772    rename_task.await.expect("Confirm rename failed");
16773    cx.run_until_parked();
16774
16775    // Correct range is renamed, as `surrounding_word` is used to find it.
16776    cx.assert_editor_state(indoc! {"
16777        struct FooRenamedˇ {}
16778    "});
16779}
16780
16781#[gpui::test]
16782async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16783    init_test(cx, |_| {});
16784    let mut cx = EditorTestContext::new(cx).await;
16785
16786    let language = Arc::new(
16787        Language::new(
16788            LanguageConfig::default(),
16789            Some(tree_sitter_html::LANGUAGE.into()),
16790        )
16791        .with_brackets_query(
16792            r#"
16793            ("<" @open "/>" @close)
16794            ("</" @open ">" @close)
16795            ("<" @open ">" @close)
16796            ("\"" @open "\"" @close)
16797            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16798        "#,
16799        )
16800        .unwrap(),
16801    );
16802    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16803
16804    cx.set_state(indoc! {"
16805        <span>ˇ</span>
16806    "});
16807    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16808    cx.assert_editor_state(indoc! {"
16809        <span>
16810        ˇ
16811        </span>
16812    "});
16813
16814    cx.set_state(indoc! {"
16815        <span><span></span>ˇ</span>
16816    "});
16817    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16818    cx.assert_editor_state(indoc! {"
16819        <span><span></span>
16820        ˇ</span>
16821    "});
16822
16823    cx.set_state(indoc! {"
16824        <span>ˇ
16825        </span>
16826    "});
16827    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16828    cx.assert_editor_state(indoc! {"
16829        <span>
16830        ˇ
16831        </span>
16832    "});
16833}
16834
16835fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16836    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16837    point..point
16838}
16839
16840fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16841    let (text, ranges) = marked_text_ranges(marked_text, true);
16842    assert_eq!(editor.text(cx), text);
16843    assert_eq!(
16844        editor.selections.ranges(cx),
16845        ranges,
16846        "Assert selections are {}",
16847        marked_text
16848    );
16849}
16850
16851pub fn handle_signature_help_request(
16852    cx: &mut EditorLspTestContext,
16853    mocked_response: lsp::SignatureHelp,
16854) -> impl Future<Output = ()> {
16855    let mut request =
16856        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16857            let mocked_response = mocked_response.clone();
16858            async move { Ok(Some(mocked_response)) }
16859        });
16860
16861    async move {
16862        request.next().await;
16863    }
16864}
16865
16866/// Handle completion request passing a marked string specifying where the completion
16867/// should be triggered from using '|' character, what range should be replaced, and what completions
16868/// should be returned using '<' and '>' to delimit the range
16869pub fn handle_completion_request(
16870    cx: &mut EditorLspTestContext,
16871    marked_string: &str,
16872    completions: Vec<&'static str>,
16873    counter: Arc<AtomicUsize>,
16874) -> impl Future<Output = ()> {
16875    let complete_from_marker: TextRangeMarker = '|'.into();
16876    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16877    let (_, mut marked_ranges) = marked_text_ranges_by(
16878        marked_string,
16879        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16880    );
16881
16882    let complete_from_position =
16883        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16884    let replace_range =
16885        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16886
16887    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16888        let completions = completions.clone();
16889        counter.fetch_add(1, atomic::Ordering::Release);
16890        async move {
16891            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16892            assert_eq!(
16893                params.text_document_position.position,
16894                complete_from_position
16895            );
16896            Ok(Some(lsp::CompletionResponse::Array(
16897                completions
16898                    .iter()
16899                    .map(|completion_text| lsp::CompletionItem {
16900                        label: completion_text.to_string(),
16901                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16902                            range: replace_range,
16903                            new_text: completion_text.to_string(),
16904                        })),
16905                        ..Default::default()
16906                    })
16907                    .collect(),
16908            )))
16909        }
16910    });
16911
16912    async move {
16913        request.next().await;
16914    }
16915}
16916
16917fn handle_resolve_completion_request(
16918    cx: &mut EditorLspTestContext,
16919    edits: Option<Vec<(&'static str, &'static str)>>,
16920) -> impl Future<Output = ()> {
16921    let edits = edits.map(|edits| {
16922        edits
16923            .iter()
16924            .map(|(marked_string, new_text)| {
16925                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16926                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16927                lsp::TextEdit::new(replace_range, new_text.to_string())
16928            })
16929            .collect::<Vec<_>>()
16930    });
16931
16932    let mut request =
16933        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16934            let edits = edits.clone();
16935            async move {
16936                Ok(lsp::CompletionItem {
16937                    additional_text_edits: edits,
16938                    ..Default::default()
16939                })
16940            }
16941        });
16942
16943    async move {
16944        request.next().await;
16945    }
16946}
16947
16948pub(crate) fn update_test_language_settings(
16949    cx: &mut TestAppContext,
16950    f: impl Fn(&mut AllLanguageSettingsContent),
16951) {
16952    cx.update(|cx| {
16953        SettingsStore::update_global(cx, |store, cx| {
16954            store.update_user_settings::<AllLanguageSettings>(cx, f);
16955        });
16956    });
16957}
16958
16959pub(crate) fn update_test_project_settings(
16960    cx: &mut TestAppContext,
16961    f: impl Fn(&mut ProjectSettings),
16962) {
16963    cx.update(|cx| {
16964        SettingsStore::update_global(cx, |store, cx| {
16965            store.update_user_settings::<ProjectSettings>(cx, f);
16966        });
16967    });
16968}
16969
16970pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16971    cx.update(|cx| {
16972        assets::Assets.load_test_fonts(cx);
16973        let store = SettingsStore::test(cx);
16974        cx.set_global(store);
16975        theme::init(theme::LoadThemes::JustBase, cx);
16976        release_channel::init(SemanticVersion::default(), cx);
16977        client::init_settings(cx);
16978        language::init(cx);
16979        Project::init_settings(cx);
16980        workspace::init_settings(cx);
16981        crate::init(cx);
16982    });
16983
16984    update_test_language_settings(cx, f);
16985}
16986
16987#[track_caller]
16988fn assert_hunk_revert(
16989    not_reverted_text_with_selections: &str,
16990    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
16991    expected_reverted_text_with_selections: &str,
16992    base_text: &str,
16993    cx: &mut EditorLspTestContext,
16994) {
16995    cx.set_state(not_reverted_text_with_selections);
16996    cx.set_head_text(base_text);
16997    cx.executor().run_until_parked();
16998
16999    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17000        let snapshot = editor.snapshot(window, cx);
17001        let reverted_hunk_statuses = snapshot
17002            .buffer_snapshot
17003            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17004            .map(|hunk| hunk.status().kind)
17005            .collect::<Vec<_>>();
17006
17007        editor.git_restore(&Default::default(), window, cx);
17008        reverted_hunk_statuses
17009    });
17010    cx.executor().run_until_parked();
17011    cx.assert_editor_state(expected_reverted_text_with_selections);
17012    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17013}