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 mut cx = EditorLspTestContext::new_rust(
12363        lsp::ServerCapabilities {
12364            completion_provider: Some(lsp::CompletionOptions {
12365                trigger_characters: Some(vec![".".to_string()]),
12366                resolve_provider: Some(true),
12367                ..Default::default()
12368            }),
12369            ..Default::default()
12370        },
12371        cx,
12372    )
12373    .await;
12374
12375    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12376    cx.simulate_keystroke(".");
12377
12378    let completion_data = default_data.clone();
12379    let completion_characters = default_commit_characters.clone();
12380    let completion_items = items.clone();
12381    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12382        let default_data = completion_data.clone();
12383        let default_commit_characters = completion_characters.clone();
12384        let items = completion_items.clone();
12385        async move {
12386            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12387                items,
12388                item_defaults: Some(lsp::CompletionListItemDefaults {
12389                    data: Some(default_data.clone()),
12390                    commit_characters: Some(default_commit_characters.clone()),
12391                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12392                        default_edit_range,
12393                    )),
12394                    insert_text_format: Some(default_insert_text_format),
12395                    insert_text_mode: Some(default_insert_text_mode),
12396                }),
12397                ..lsp::CompletionList::default()
12398            })))
12399        }
12400    })
12401    .next()
12402    .await;
12403
12404    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12405    cx.lsp
12406        .server
12407        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12408            let closure_resolved_items = resolved_items.clone();
12409            move |item_to_resolve, _| {
12410                let closure_resolved_items = closure_resolved_items.clone();
12411                async move {
12412                    closure_resolved_items.lock().push(item_to_resolve.clone());
12413                    Ok(item_to_resolve)
12414                }
12415            }
12416        })
12417        .detach();
12418
12419    cx.condition(|editor, _| editor.context_menu_visible())
12420        .await;
12421    cx.run_until_parked();
12422    cx.update_editor(|editor, _, _| {
12423        let menu = editor.context_menu.borrow_mut();
12424        match menu.as_ref().expect("should have the completions menu") {
12425            CodeContextMenu::Completions(completions_menu) => {
12426                assert_eq!(
12427                    completions_menu
12428                        .entries
12429                        .borrow()
12430                        .iter()
12431                        .map(|mat| mat.string.clone())
12432                        .collect::<Vec<String>>(),
12433                    items
12434                        .iter()
12435                        .map(|completion| completion.label.clone())
12436                        .collect::<Vec<String>>()
12437                );
12438            }
12439            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12440        }
12441    });
12442    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12443    // with 4 from the end.
12444    assert_eq!(
12445        *resolved_items.lock(),
12446        [&items[0..16], &items[items.len() - 4..items.len()]]
12447            .concat()
12448            .iter()
12449            .cloned()
12450            .map(|mut item| {
12451                if item.data.is_none() {
12452                    item.data = Some(default_data.clone());
12453                }
12454                item
12455            })
12456            .collect::<Vec<lsp::CompletionItem>>(),
12457        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12458    );
12459    resolved_items.lock().clear();
12460
12461    cx.update_editor(|editor, window, cx| {
12462        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12463    });
12464    cx.run_until_parked();
12465    // Completions that have already been resolved are skipped.
12466    assert_eq!(
12467        *resolved_items.lock(),
12468        items[items.len() - 16..items.len() - 4]
12469            .iter()
12470            .cloned()
12471            .map(|mut item| {
12472                if item.data.is_none() {
12473                    item.data = Some(default_data.clone());
12474                }
12475                item
12476            })
12477            .collect::<Vec<lsp::CompletionItem>>()
12478    );
12479    resolved_items.lock().clear();
12480}
12481
12482#[gpui::test]
12483async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12484    init_test(cx, |_| {});
12485
12486    let mut cx = EditorLspTestContext::new(
12487        Language::new(
12488            LanguageConfig {
12489                matcher: LanguageMatcher {
12490                    path_suffixes: vec!["jsx".into()],
12491                    ..Default::default()
12492                },
12493                overrides: [(
12494                    "element".into(),
12495                    LanguageConfigOverride {
12496                        word_characters: Override::Set(['-'].into_iter().collect()),
12497                        ..Default::default()
12498                    },
12499                )]
12500                .into_iter()
12501                .collect(),
12502                ..Default::default()
12503            },
12504            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12505        )
12506        .with_override_query("(jsx_self_closing_element) @element")
12507        .unwrap(),
12508        lsp::ServerCapabilities {
12509            completion_provider: Some(lsp::CompletionOptions {
12510                trigger_characters: Some(vec![":".to_string()]),
12511                ..Default::default()
12512            }),
12513            ..Default::default()
12514        },
12515        cx,
12516    )
12517    .await;
12518
12519    cx.lsp
12520        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12521            Ok(Some(lsp::CompletionResponse::Array(vec![
12522                lsp::CompletionItem {
12523                    label: "bg-blue".into(),
12524                    ..Default::default()
12525                },
12526                lsp::CompletionItem {
12527                    label: "bg-red".into(),
12528                    ..Default::default()
12529                },
12530                lsp::CompletionItem {
12531                    label: "bg-yellow".into(),
12532                    ..Default::default()
12533                },
12534            ])))
12535        });
12536
12537    cx.set_state(r#"<p class="bgˇ" />"#);
12538
12539    // Trigger completion when typing a dash, because the dash is an extra
12540    // word character in the 'element' scope, which contains the cursor.
12541    cx.simulate_keystroke("-");
12542    cx.executor().run_until_parked();
12543    cx.update_editor(|editor, _, _| {
12544        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12545        {
12546            assert_eq!(
12547                completion_menu_entries(&menu),
12548                &["bg-red", "bg-blue", "bg-yellow"]
12549            );
12550        } else {
12551            panic!("expected completion menu to be open");
12552        }
12553    });
12554
12555    cx.simulate_keystroke("l");
12556    cx.executor().run_until_parked();
12557    cx.update_editor(|editor, _, _| {
12558        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12559        {
12560            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12561        } else {
12562            panic!("expected completion menu to be open");
12563        }
12564    });
12565
12566    // When filtering completions, consider the character after the '-' to
12567    // be the start of a subword.
12568    cx.set_state(r#"<p class="yelˇ" />"#);
12569    cx.simulate_keystroke("l");
12570    cx.executor().run_until_parked();
12571    cx.update_editor(|editor, _, _| {
12572        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12573        {
12574            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12575        } else {
12576            panic!("expected completion menu to be open");
12577        }
12578    });
12579}
12580
12581fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12582    let entries = menu.entries.borrow();
12583    entries.iter().map(|mat| mat.string.clone()).collect()
12584}
12585
12586#[gpui::test]
12587async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12588    init_test(cx, |settings| {
12589        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12590            FormatterList(vec![Formatter::Prettier].into()),
12591        ))
12592    });
12593
12594    let fs = FakeFs::new(cx.executor());
12595    fs.insert_file(path!("/file.ts"), Default::default()).await;
12596
12597    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12598    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12599
12600    language_registry.add(Arc::new(Language::new(
12601        LanguageConfig {
12602            name: "TypeScript".into(),
12603            matcher: LanguageMatcher {
12604                path_suffixes: vec!["ts".to_string()],
12605                ..Default::default()
12606            },
12607            ..Default::default()
12608        },
12609        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12610    )));
12611    update_test_language_settings(cx, |settings| {
12612        settings.defaults.prettier = Some(PrettierSettings {
12613            allowed: true,
12614            ..PrettierSettings::default()
12615        });
12616    });
12617
12618    let test_plugin = "test_plugin";
12619    let _ = language_registry.register_fake_lsp(
12620        "TypeScript",
12621        FakeLspAdapter {
12622            prettier_plugins: vec![test_plugin],
12623            ..Default::default()
12624        },
12625    );
12626
12627    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12628    let buffer = project
12629        .update(cx, |project, cx| {
12630            project.open_local_buffer(path!("/file.ts"), cx)
12631        })
12632        .await
12633        .unwrap();
12634
12635    let buffer_text = "one\ntwo\nthree\n";
12636    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12637    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12638    editor.update_in(cx, |editor, window, cx| {
12639        editor.set_text(buffer_text, window, cx)
12640    });
12641
12642    editor
12643        .update_in(cx, |editor, window, cx| {
12644            editor.perform_format(
12645                project.clone(),
12646                FormatTrigger::Manual,
12647                FormatTarget::Buffers,
12648                window,
12649                cx,
12650            )
12651        })
12652        .unwrap()
12653        .await;
12654    assert_eq!(
12655        editor.update(cx, |editor, cx| editor.text(cx)),
12656        buffer_text.to_string() + prettier_format_suffix,
12657        "Test prettier formatting was not applied to the original buffer text",
12658    );
12659
12660    update_test_language_settings(cx, |settings| {
12661        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12662    });
12663    let format = editor.update_in(cx, |editor, window, cx| {
12664        editor.perform_format(
12665            project.clone(),
12666            FormatTrigger::Manual,
12667            FormatTarget::Buffers,
12668            window,
12669            cx,
12670        )
12671    });
12672    format.await.unwrap();
12673    assert_eq!(
12674        editor.update(cx, |editor, cx| editor.text(cx)),
12675        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12676        "Autoformatting (via test prettier) was not applied to the original buffer text",
12677    );
12678}
12679
12680#[gpui::test]
12681async fn test_addition_reverts(cx: &mut TestAppContext) {
12682    init_test(cx, |_| {});
12683    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12684    let base_text = indoc! {r#"
12685        struct Row;
12686        struct Row1;
12687        struct Row2;
12688
12689        struct Row4;
12690        struct Row5;
12691        struct Row6;
12692
12693        struct Row8;
12694        struct Row9;
12695        struct Row10;"#};
12696
12697    // When addition hunks are not adjacent to carets, no hunk revert is performed
12698    assert_hunk_revert(
12699        indoc! {r#"struct Row;
12700                   struct Row1;
12701                   struct Row1.1;
12702                   struct Row1.2;
12703                   struct Row2;ˇ
12704
12705                   struct Row4;
12706                   struct Row5;
12707                   struct Row6;
12708
12709                   struct Row8;
12710                   ˇstruct Row9;
12711                   struct Row9.1;
12712                   struct Row9.2;
12713                   struct Row9.3;
12714                   struct Row10;"#},
12715        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12716        indoc! {r#"struct Row;
12717                   struct Row1;
12718                   struct Row1.1;
12719                   struct Row1.2;
12720                   struct Row2;ˇ
12721
12722                   struct Row4;
12723                   struct Row5;
12724                   struct Row6;
12725
12726                   struct Row8;
12727                   ˇstruct Row9;
12728                   struct Row9.1;
12729                   struct Row9.2;
12730                   struct Row9.3;
12731                   struct Row10;"#},
12732        base_text,
12733        &mut cx,
12734    );
12735    // Same for selections
12736    assert_hunk_revert(
12737        indoc! {r#"struct Row;
12738                   struct Row1;
12739                   struct Row2;
12740                   struct Row2.1;
12741                   struct Row2.2;
12742                   «ˇ
12743                   struct Row4;
12744                   struct» Row5;
12745                   «struct Row6;
12746                   ˇ»
12747                   struct Row9.1;
12748                   struct Row9.2;
12749                   struct Row9.3;
12750                   struct Row8;
12751                   struct Row9;
12752                   struct Row10;"#},
12753        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12754        indoc! {r#"struct Row;
12755                   struct Row1;
12756                   struct Row2;
12757                   struct Row2.1;
12758                   struct Row2.2;
12759                   «ˇ
12760                   struct Row4;
12761                   struct» Row5;
12762                   «struct Row6;
12763                   ˇ»
12764                   struct Row9.1;
12765                   struct Row9.2;
12766                   struct Row9.3;
12767                   struct Row8;
12768                   struct Row9;
12769                   struct Row10;"#},
12770        base_text,
12771        &mut cx,
12772    );
12773
12774    // When carets and selections intersect the addition hunks, those are reverted.
12775    // Adjacent carets got merged.
12776    assert_hunk_revert(
12777        indoc! {r#"struct Row;
12778                   ˇ// something on the top
12779                   struct Row1;
12780                   struct Row2;
12781                   struct Roˇw3.1;
12782                   struct Row2.2;
12783                   struct Row2.3;ˇ
12784
12785                   struct Row4;
12786                   struct ˇRow5.1;
12787                   struct Row5.2;
12788                   struct «Rowˇ»5.3;
12789                   struct Row5;
12790                   struct Row6;
12791                   ˇ
12792                   struct Row9.1;
12793                   struct «Rowˇ»9.2;
12794                   struct «ˇRow»9.3;
12795                   struct Row8;
12796                   struct Row9;
12797                   «ˇ// something on bottom»
12798                   struct Row10;"#},
12799        vec![
12800            DiffHunkStatusKind::Added,
12801            DiffHunkStatusKind::Added,
12802            DiffHunkStatusKind::Added,
12803            DiffHunkStatusKind::Added,
12804            DiffHunkStatusKind::Added,
12805        ],
12806        indoc! {r#"struct Row;
12807                   ˇstruct Row1;
12808                   struct Row2;
12809                   ˇ
12810                   struct Row4;
12811                   ˇstruct Row5;
12812                   struct Row6;
12813                   ˇ
12814                   ˇstruct Row8;
12815                   struct Row9;
12816                   ˇstruct Row10;"#},
12817        base_text,
12818        &mut cx,
12819    );
12820}
12821
12822#[gpui::test]
12823async fn test_modification_reverts(cx: &mut TestAppContext) {
12824    init_test(cx, |_| {});
12825    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12826    let base_text = indoc! {r#"
12827        struct Row;
12828        struct Row1;
12829        struct Row2;
12830
12831        struct Row4;
12832        struct Row5;
12833        struct Row6;
12834
12835        struct Row8;
12836        struct Row9;
12837        struct Row10;"#};
12838
12839    // Modification hunks behave the same as the addition ones.
12840    assert_hunk_revert(
12841        indoc! {r#"struct Row;
12842                   struct Row1;
12843                   struct Row33;
12844                   ˇ
12845                   struct Row4;
12846                   struct Row5;
12847                   struct Row6;
12848                   ˇ
12849                   struct Row99;
12850                   struct Row9;
12851                   struct Row10;"#},
12852        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12853        indoc! {r#"struct Row;
12854                   struct Row1;
12855                   struct Row33;
12856                   ˇ
12857                   struct Row4;
12858                   struct Row5;
12859                   struct Row6;
12860                   ˇ
12861                   struct Row99;
12862                   struct Row9;
12863                   struct Row10;"#},
12864        base_text,
12865        &mut cx,
12866    );
12867    assert_hunk_revert(
12868        indoc! {r#"struct Row;
12869                   struct Row1;
12870                   struct Row33;
12871                   «ˇ
12872                   struct Row4;
12873                   struct» Row5;
12874                   «struct Row6;
12875                   ˇ»
12876                   struct Row99;
12877                   struct Row9;
12878                   struct Row10;"#},
12879        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12880        indoc! {r#"struct Row;
12881                   struct Row1;
12882                   struct Row33;
12883                   «ˇ
12884                   struct Row4;
12885                   struct» Row5;
12886                   «struct Row6;
12887                   ˇ»
12888                   struct Row99;
12889                   struct Row9;
12890                   struct Row10;"#},
12891        base_text,
12892        &mut cx,
12893    );
12894
12895    assert_hunk_revert(
12896        indoc! {r#"ˇstruct Row1.1;
12897                   struct Row1;
12898                   «ˇstr»uct Row22;
12899
12900                   struct ˇRow44;
12901                   struct Row5;
12902                   struct «Rˇ»ow66;ˇ
12903
12904                   «struˇ»ct Row88;
12905                   struct Row9;
12906                   struct Row1011;ˇ"#},
12907        vec![
12908            DiffHunkStatusKind::Modified,
12909            DiffHunkStatusKind::Modified,
12910            DiffHunkStatusKind::Modified,
12911            DiffHunkStatusKind::Modified,
12912            DiffHunkStatusKind::Modified,
12913            DiffHunkStatusKind::Modified,
12914        ],
12915        indoc! {r#"struct Row;
12916                   ˇstruct Row1;
12917                   struct Row2;
12918                   ˇ
12919                   struct Row4;
12920                   ˇstruct Row5;
12921                   struct Row6;
12922                   ˇ
12923                   struct Row8;
12924                   ˇstruct Row9;
12925                   struct Row10;ˇ"#},
12926        base_text,
12927        &mut cx,
12928    );
12929}
12930
12931#[gpui::test]
12932async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12933    init_test(cx, |_| {});
12934    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12935    let base_text = indoc! {r#"
12936        one
12937
12938        two
12939        three
12940        "#};
12941
12942    cx.set_head_text(base_text);
12943    cx.set_state("\nˇ\n");
12944    cx.executor().run_until_parked();
12945    cx.update_editor(|editor, _window, cx| {
12946        editor.expand_selected_diff_hunks(cx);
12947    });
12948    cx.executor().run_until_parked();
12949    cx.update_editor(|editor, window, cx| {
12950        editor.backspace(&Default::default(), window, cx);
12951    });
12952    cx.run_until_parked();
12953    cx.assert_state_with_diff(
12954        indoc! {r#"
12955
12956        - two
12957        - threeˇ
12958        +
12959        "#}
12960        .to_string(),
12961    );
12962}
12963
12964#[gpui::test]
12965async fn test_deletion_reverts(cx: &mut TestAppContext) {
12966    init_test(cx, |_| {});
12967    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12968    let base_text = indoc! {r#"struct Row;
12969struct Row1;
12970struct Row2;
12971
12972struct Row4;
12973struct Row5;
12974struct Row6;
12975
12976struct Row8;
12977struct Row9;
12978struct Row10;"#};
12979
12980    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12981    assert_hunk_revert(
12982        indoc! {r#"struct Row;
12983                   struct Row2;
12984
12985                   ˇstruct Row4;
12986                   struct Row5;
12987                   struct Row6;
12988                   ˇ
12989                   struct Row8;
12990                   struct Row10;"#},
12991        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12992        indoc! {r#"struct Row;
12993                   struct Row2;
12994
12995                   ˇstruct Row4;
12996                   struct Row5;
12997                   struct Row6;
12998                   ˇ
12999                   struct Row8;
13000                   struct Row10;"#},
13001        base_text,
13002        &mut cx,
13003    );
13004    assert_hunk_revert(
13005        indoc! {r#"struct Row;
13006                   struct Row2;
13007
13008                   «ˇstruct Row4;
13009                   struct» Row5;
13010                   «struct Row6;
13011                   ˇ»
13012                   struct Row8;
13013                   struct Row10;"#},
13014        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13015        indoc! {r#"struct Row;
13016                   struct Row2;
13017
13018                   «ˇstruct Row4;
13019                   struct» Row5;
13020                   «struct Row6;
13021                   ˇ»
13022                   struct Row8;
13023                   struct Row10;"#},
13024        base_text,
13025        &mut cx,
13026    );
13027
13028    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13029    assert_hunk_revert(
13030        indoc! {r#"struct Row;
13031                   ˇstruct Row2;
13032
13033                   struct Row4;
13034                   struct Row5;
13035                   struct Row6;
13036
13037                   struct Row8;ˇ
13038                   struct Row10;"#},
13039        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13040        indoc! {r#"struct Row;
13041                   struct Row1;
13042                   ˇstruct Row2;
13043
13044                   struct Row4;
13045                   struct Row5;
13046                   struct Row6;
13047
13048                   struct Row8;ˇ
13049                   struct Row9;
13050                   struct Row10;"#},
13051        base_text,
13052        &mut cx,
13053    );
13054    assert_hunk_revert(
13055        indoc! {r#"struct Row;
13056                   struct Row2«ˇ;
13057                   struct Row4;
13058                   struct» Row5;
13059                   «struct Row6;
13060
13061                   struct Row8;ˇ»
13062                   struct Row10;"#},
13063        vec![
13064            DiffHunkStatusKind::Deleted,
13065            DiffHunkStatusKind::Deleted,
13066            DiffHunkStatusKind::Deleted,
13067        ],
13068        indoc! {r#"struct Row;
13069                   struct Row1;
13070                   struct Row2«ˇ;
13071
13072                   struct Row4;
13073                   struct» Row5;
13074                   «struct Row6;
13075
13076                   struct Row8;ˇ»
13077                   struct Row9;
13078                   struct Row10;"#},
13079        base_text,
13080        &mut cx,
13081    );
13082}
13083
13084#[gpui::test]
13085async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13086    init_test(cx, |_| {});
13087
13088    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13089    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13090    let base_text_3 =
13091        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13092
13093    let text_1 = edit_first_char_of_every_line(base_text_1);
13094    let text_2 = edit_first_char_of_every_line(base_text_2);
13095    let text_3 = edit_first_char_of_every_line(base_text_3);
13096
13097    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13098    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13099    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13100
13101    let multibuffer = cx.new(|cx| {
13102        let mut multibuffer = MultiBuffer::new(ReadWrite);
13103        multibuffer.push_excerpts(
13104            buffer_1.clone(),
13105            [
13106                ExcerptRange {
13107                    context: Point::new(0, 0)..Point::new(3, 0),
13108                    primary: None,
13109                },
13110                ExcerptRange {
13111                    context: Point::new(5, 0)..Point::new(7, 0),
13112                    primary: None,
13113                },
13114                ExcerptRange {
13115                    context: Point::new(9, 0)..Point::new(10, 4),
13116                    primary: None,
13117                },
13118            ],
13119            cx,
13120        );
13121        multibuffer.push_excerpts(
13122            buffer_2.clone(),
13123            [
13124                ExcerptRange {
13125                    context: Point::new(0, 0)..Point::new(3, 0),
13126                    primary: None,
13127                },
13128                ExcerptRange {
13129                    context: Point::new(5, 0)..Point::new(7, 0),
13130                    primary: None,
13131                },
13132                ExcerptRange {
13133                    context: Point::new(9, 0)..Point::new(10, 4),
13134                    primary: None,
13135                },
13136            ],
13137            cx,
13138        );
13139        multibuffer.push_excerpts(
13140            buffer_3.clone(),
13141            [
13142                ExcerptRange {
13143                    context: Point::new(0, 0)..Point::new(3, 0),
13144                    primary: None,
13145                },
13146                ExcerptRange {
13147                    context: Point::new(5, 0)..Point::new(7, 0),
13148                    primary: None,
13149                },
13150                ExcerptRange {
13151                    context: Point::new(9, 0)..Point::new(10, 4),
13152                    primary: None,
13153                },
13154            ],
13155            cx,
13156        );
13157        multibuffer
13158    });
13159
13160    let fs = FakeFs::new(cx.executor());
13161    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13162    let (editor, cx) = cx
13163        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13164    editor.update_in(cx, |editor, _window, cx| {
13165        for (buffer, diff_base) in [
13166            (buffer_1.clone(), base_text_1),
13167            (buffer_2.clone(), base_text_2),
13168            (buffer_3.clone(), base_text_3),
13169        ] {
13170            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13171            editor
13172                .buffer
13173                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13174        }
13175    });
13176    cx.executor().run_until_parked();
13177
13178    editor.update_in(cx, |editor, window, cx| {
13179        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}");
13180        editor.select_all(&SelectAll, window, cx);
13181        editor.git_restore(&Default::default(), window, cx);
13182    });
13183    cx.executor().run_until_parked();
13184
13185    // When all ranges are selected, all buffer hunks are reverted.
13186    editor.update(cx, |editor, cx| {
13187        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");
13188    });
13189    buffer_1.update(cx, |buffer, _| {
13190        assert_eq!(buffer.text(), base_text_1);
13191    });
13192    buffer_2.update(cx, |buffer, _| {
13193        assert_eq!(buffer.text(), base_text_2);
13194    });
13195    buffer_3.update(cx, |buffer, _| {
13196        assert_eq!(buffer.text(), base_text_3);
13197    });
13198
13199    editor.update_in(cx, |editor, window, cx| {
13200        editor.undo(&Default::default(), window, cx);
13201    });
13202
13203    editor.update_in(cx, |editor, window, cx| {
13204        editor.change_selections(None, window, cx, |s| {
13205            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13206        });
13207        editor.git_restore(&Default::default(), window, cx);
13208    });
13209
13210    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13211    // but not affect buffer_2 and its related excerpts.
13212    editor.update(cx, |editor, cx| {
13213        assert_eq!(
13214            editor.text(cx),
13215            "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}"
13216        );
13217    });
13218    buffer_1.update(cx, |buffer, _| {
13219        assert_eq!(buffer.text(), base_text_1);
13220    });
13221    buffer_2.update(cx, |buffer, _| {
13222        assert_eq!(
13223            buffer.text(),
13224            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13225        );
13226    });
13227    buffer_3.update(cx, |buffer, _| {
13228        assert_eq!(
13229            buffer.text(),
13230            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13231        );
13232    });
13233
13234    fn edit_first_char_of_every_line(text: &str) -> String {
13235        text.split('\n')
13236            .map(|line| format!("X{}", &line[1..]))
13237            .collect::<Vec<_>>()
13238            .join("\n")
13239    }
13240}
13241
13242#[gpui::test]
13243async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13244    init_test(cx, |_| {});
13245
13246    let cols = 4;
13247    let rows = 10;
13248    let sample_text_1 = sample_text(rows, cols, 'a');
13249    assert_eq!(
13250        sample_text_1,
13251        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13252    );
13253    let sample_text_2 = sample_text(rows, cols, 'l');
13254    assert_eq!(
13255        sample_text_2,
13256        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13257    );
13258    let sample_text_3 = sample_text(rows, cols, 'v');
13259    assert_eq!(
13260        sample_text_3,
13261        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13262    );
13263
13264    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13265    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13266    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13267
13268    let multi_buffer = cx.new(|cx| {
13269        let mut multibuffer = MultiBuffer::new(ReadWrite);
13270        multibuffer.push_excerpts(
13271            buffer_1.clone(),
13272            [
13273                ExcerptRange {
13274                    context: Point::new(0, 0)..Point::new(3, 0),
13275                    primary: None,
13276                },
13277                ExcerptRange {
13278                    context: Point::new(5, 0)..Point::new(7, 0),
13279                    primary: None,
13280                },
13281                ExcerptRange {
13282                    context: Point::new(9, 0)..Point::new(10, 4),
13283                    primary: None,
13284                },
13285            ],
13286            cx,
13287        );
13288        multibuffer.push_excerpts(
13289            buffer_2.clone(),
13290            [
13291                ExcerptRange {
13292                    context: Point::new(0, 0)..Point::new(3, 0),
13293                    primary: None,
13294                },
13295                ExcerptRange {
13296                    context: Point::new(5, 0)..Point::new(7, 0),
13297                    primary: None,
13298                },
13299                ExcerptRange {
13300                    context: Point::new(9, 0)..Point::new(10, 4),
13301                    primary: None,
13302                },
13303            ],
13304            cx,
13305        );
13306        multibuffer.push_excerpts(
13307            buffer_3.clone(),
13308            [
13309                ExcerptRange {
13310                    context: Point::new(0, 0)..Point::new(3, 0),
13311                    primary: None,
13312                },
13313                ExcerptRange {
13314                    context: Point::new(5, 0)..Point::new(7, 0),
13315                    primary: None,
13316                },
13317                ExcerptRange {
13318                    context: Point::new(9, 0)..Point::new(10, 4),
13319                    primary: None,
13320                },
13321            ],
13322            cx,
13323        );
13324        multibuffer
13325    });
13326
13327    let fs = FakeFs::new(cx.executor());
13328    fs.insert_tree(
13329        "/a",
13330        json!({
13331            "main.rs": sample_text_1,
13332            "other.rs": sample_text_2,
13333            "lib.rs": sample_text_3,
13334        }),
13335    )
13336    .await;
13337    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13338    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13339    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13340    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13341        Editor::new(
13342            EditorMode::Full,
13343            multi_buffer,
13344            Some(project.clone()),
13345            true,
13346            window,
13347            cx,
13348        )
13349    });
13350    let multibuffer_item_id = workspace
13351        .update(cx, |workspace, window, cx| {
13352            assert!(
13353                workspace.active_item(cx).is_none(),
13354                "active item should be None before the first item is added"
13355            );
13356            workspace.add_item_to_active_pane(
13357                Box::new(multi_buffer_editor.clone()),
13358                None,
13359                true,
13360                window,
13361                cx,
13362            );
13363            let active_item = workspace
13364                .active_item(cx)
13365                .expect("should have an active item after adding the multi buffer");
13366            assert!(
13367                !active_item.is_singleton(cx),
13368                "A multi buffer was expected to active after adding"
13369            );
13370            active_item.item_id()
13371        })
13372        .unwrap();
13373    cx.executor().run_until_parked();
13374
13375    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13376        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13377            s.select_ranges(Some(1..2))
13378        });
13379        editor.open_excerpts(&OpenExcerpts, window, cx);
13380    });
13381    cx.executor().run_until_parked();
13382    let first_item_id = workspace
13383        .update(cx, |workspace, window, cx| {
13384            let active_item = workspace
13385                .active_item(cx)
13386                .expect("should have an active item after navigating into the 1st buffer");
13387            let first_item_id = active_item.item_id();
13388            assert_ne!(
13389                first_item_id, multibuffer_item_id,
13390                "Should navigate into the 1st buffer and activate it"
13391            );
13392            assert!(
13393                active_item.is_singleton(cx),
13394                "New active item should be a singleton buffer"
13395            );
13396            assert_eq!(
13397                active_item
13398                    .act_as::<Editor>(cx)
13399                    .expect("should have navigated into an editor for the 1st buffer")
13400                    .read(cx)
13401                    .text(cx),
13402                sample_text_1
13403            );
13404
13405            workspace
13406                .go_back(workspace.active_pane().downgrade(), window, cx)
13407                .detach_and_log_err(cx);
13408
13409            first_item_id
13410        })
13411        .unwrap();
13412    cx.executor().run_until_parked();
13413    workspace
13414        .update(cx, |workspace, _, cx| {
13415            let active_item = workspace
13416                .active_item(cx)
13417                .expect("should have an active item after navigating back");
13418            assert_eq!(
13419                active_item.item_id(),
13420                multibuffer_item_id,
13421                "Should navigate back to the multi buffer"
13422            );
13423            assert!(!active_item.is_singleton(cx));
13424        })
13425        .unwrap();
13426
13427    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13428        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13429            s.select_ranges(Some(39..40))
13430        });
13431        editor.open_excerpts(&OpenExcerpts, window, cx);
13432    });
13433    cx.executor().run_until_parked();
13434    let second_item_id = workspace
13435        .update(cx, |workspace, window, cx| {
13436            let active_item = workspace
13437                .active_item(cx)
13438                .expect("should have an active item after navigating into the 2nd buffer");
13439            let second_item_id = active_item.item_id();
13440            assert_ne!(
13441                second_item_id, multibuffer_item_id,
13442                "Should navigate away from the multibuffer"
13443            );
13444            assert_ne!(
13445                second_item_id, first_item_id,
13446                "Should navigate into the 2nd buffer and activate it"
13447            );
13448            assert!(
13449                active_item.is_singleton(cx),
13450                "New active item should be a singleton buffer"
13451            );
13452            assert_eq!(
13453                active_item
13454                    .act_as::<Editor>(cx)
13455                    .expect("should have navigated into an editor")
13456                    .read(cx)
13457                    .text(cx),
13458                sample_text_2
13459            );
13460
13461            workspace
13462                .go_back(workspace.active_pane().downgrade(), window, cx)
13463                .detach_and_log_err(cx);
13464
13465            second_item_id
13466        })
13467        .unwrap();
13468    cx.executor().run_until_parked();
13469    workspace
13470        .update(cx, |workspace, _, cx| {
13471            let active_item = workspace
13472                .active_item(cx)
13473                .expect("should have an active item after navigating back from the 2nd buffer");
13474            assert_eq!(
13475                active_item.item_id(),
13476                multibuffer_item_id,
13477                "Should navigate back from the 2nd buffer to the multi buffer"
13478            );
13479            assert!(!active_item.is_singleton(cx));
13480        })
13481        .unwrap();
13482
13483    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13484        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13485            s.select_ranges(Some(70..70))
13486        });
13487        editor.open_excerpts(&OpenExcerpts, window, cx);
13488    });
13489    cx.executor().run_until_parked();
13490    workspace
13491        .update(cx, |workspace, window, cx| {
13492            let active_item = workspace
13493                .active_item(cx)
13494                .expect("should have an active item after navigating into the 3rd buffer");
13495            let third_item_id = active_item.item_id();
13496            assert_ne!(
13497                third_item_id, multibuffer_item_id,
13498                "Should navigate into the 3rd buffer and activate it"
13499            );
13500            assert_ne!(third_item_id, first_item_id);
13501            assert_ne!(third_item_id, second_item_id);
13502            assert!(
13503                active_item.is_singleton(cx),
13504                "New active item should be a singleton buffer"
13505            );
13506            assert_eq!(
13507                active_item
13508                    .act_as::<Editor>(cx)
13509                    .expect("should have navigated into an editor")
13510                    .read(cx)
13511                    .text(cx),
13512                sample_text_3
13513            );
13514
13515            workspace
13516                .go_back(workspace.active_pane().downgrade(), window, cx)
13517                .detach_and_log_err(cx);
13518        })
13519        .unwrap();
13520    cx.executor().run_until_parked();
13521    workspace
13522        .update(cx, |workspace, _, cx| {
13523            let active_item = workspace
13524                .active_item(cx)
13525                .expect("should have an active item after navigating back from the 3rd buffer");
13526            assert_eq!(
13527                active_item.item_id(),
13528                multibuffer_item_id,
13529                "Should navigate back from the 3rd buffer to the multi buffer"
13530            );
13531            assert!(!active_item.is_singleton(cx));
13532        })
13533        .unwrap();
13534}
13535
13536#[gpui::test]
13537async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13538    init_test(cx, |_| {});
13539
13540    let mut cx = EditorTestContext::new(cx).await;
13541
13542    let diff_base = r#"
13543        use some::mod;
13544
13545        const A: u32 = 42;
13546
13547        fn main() {
13548            println!("hello");
13549
13550            println!("world");
13551        }
13552        "#
13553    .unindent();
13554
13555    cx.set_state(
13556        &r#"
13557        use some::modified;
13558
13559        ˇ
13560        fn main() {
13561            println!("hello there");
13562
13563            println!("around the");
13564            println!("world");
13565        }
13566        "#
13567        .unindent(),
13568    );
13569
13570    cx.set_head_text(&diff_base);
13571    executor.run_until_parked();
13572
13573    cx.update_editor(|editor, window, cx| {
13574        editor.go_to_next_hunk(&GoToHunk, window, cx);
13575        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13576    });
13577    executor.run_until_parked();
13578    cx.assert_state_with_diff(
13579        r#"
13580          use some::modified;
13581
13582
13583          fn main() {
13584        -     println!("hello");
13585        + ˇ    println!("hello there");
13586
13587              println!("around the");
13588              println!("world");
13589          }
13590        "#
13591        .unindent(),
13592    );
13593
13594    cx.update_editor(|editor, window, cx| {
13595        for _ in 0..2 {
13596            editor.go_to_next_hunk(&GoToHunk, window, cx);
13597            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13598        }
13599    });
13600    executor.run_until_parked();
13601    cx.assert_state_with_diff(
13602        r#"
13603        - use some::mod;
13604        + ˇuse some::modified;
13605
13606
13607          fn main() {
13608        -     println!("hello");
13609        +     println!("hello there");
13610
13611        +     println!("around the");
13612              println!("world");
13613          }
13614        "#
13615        .unindent(),
13616    );
13617
13618    cx.update_editor(|editor, window, cx| {
13619        editor.go_to_next_hunk(&GoToHunk, window, cx);
13620        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13621    });
13622    executor.run_until_parked();
13623    cx.assert_state_with_diff(
13624        r#"
13625        - use some::mod;
13626        + use some::modified;
13627
13628        - const A: u32 = 42;
13629          ˇ
13630          fn main() {
13631        -     println!("hello");
13632        +     println!("hello there");
13633
13634        +     println!("around the");
13635              println!("world");
13636          }
13637        "#
13638        .unindent(),
13639    );
13640
13641    cx.update_editor(|editor, window, cx| {
13642        editor.cancel(&Cancel, window, cx);
13643    });
13644
13645    cx.assert_state_with_diff(
13646        r#"
13647          use some::modified;
13648
13649          ˇ
13650          fn main() {
13651              println!("hello there");
13652
13653              println!("around the");
13654              println!("world");
13655          }
13656        "#
13657        .unindent(),
13658    );
13659}
13660
13661#[gpui::test]
13662async fn test_diff_base_change_with_expanded_diff_hunks(
13663    executor: BackgroundExecutor,
13664    cx: &mut TestAppContext,
13665) {
13666    init_test(cx, |_| {});
13667
13668    let mut cx = EditorTestContext::new(cx).await;
13669
13670    let diff_base = r#"
13671        use some::mod1;
13672        use some::mod2;
13673
13674        const A: u32 = 42;
13675        const B: u32 = 42;
13676        const C: u32 = 42;
13677
13678        fn main() {
13679            println!("hello");
13680
13681            println!("world");
13682        }
13683        "#
13684    .unindent();
13685
13686    cx.set_state(
13687        &r#"
13688        use some::mod2;
13689
13690        const A: u32 = 42;
13691        const C: u32 = 42;
13692
13693        fn main(ˇ) {
13694            //println!("hello");
13695
13696            println!("world");
13697            //
13698            //
13699        }
13700        "#
13701        .unindent(),
13702    );
13703
13704    cx.set_head_text(&diff_base);
13705    executor.run_until_parked();
13706
13707    cx.update_editor(|editor, window, cx| {
13708        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13709    });
13710    executor.run_until_parked();
13711    cx.assert_state_with_diff(
13712        r#"
13713        - use some::mod1;
13714          use some::mod2;
13715
13716          const A: u32 = 42;
13717        - const B: u32 = 42;
13718          const C: u32 = 42;
13719
13720          fn main(ˇ) {
13721        -     println!("hello");
13722        +     //println!("hello");
13723
13724              println!("world");
13725        +     //
13726        +     //
13727          }
13728        "#
13729        .unindent(),
13730    );
13731
13732    cx.set_head_text("new diff base!");
13733    executor.run_until_parked();
13734    cx.assert_state_with_diff(
13735        r#"
13736        - new diff base!
13737        + use some::mod2;
13738        +
13739        + const A: u32 = 42;
13740        + const C: u32 = 42;
13741        +
13742        + fn main(ˇ) {
13743        +     //println!("hello");
13744        +
13745        +     println!("world");
13746        +     //
13747        +     //
13748        + }
13749        "#
13750        .unindent(),
13751    );
13752}
13753
13754#[gpui::test]
13755async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13756    init_test(cx, |_| {});
13757
13758    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13759    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13760    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13761    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13762    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13763    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13764
13765    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13766    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13767    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13768
13769    let multi_buffer = cx.new(|cx| {
13770        let mut multibuffer = MultiBuffer::new(ReadWrite);
13771        multibuffer.push_excerpts(
13772            buffer_1.clone(),
13773            [
13774                ExcerptRange {
13775                    context: Point::new(0, 0)..Point::new(3, 0),
13776                    primary: None,
13777                },
13778                ExcerptRange {
13779                    context: Point::new(5, 0)..Point::new(7, 0),
13780                    primary: None,
13781                },
13782                ExcerptRange {
13783                    context: Point::new(9, 0)..Point::new(10, 3),
13784                    primary: None,
13785                },
13786            ],
13787            cx,
13788        );
13789        multibuffer.push_excerpts(
13790            buffer_2.clone(),
13791            [
13792                ExcerptRange {
13793                    context: Point::new(0, 0)..Point::new(3, 0),
13794                    primary: None,
13795                },
13796                ExcerptRange {
13797                    context: Point::new(5, 0)..Point::new(7, 0),
13798                    primary: None,
13799                },
13800                ExcerptRange {
13801                    context: Point::new(9, 0)..Point::new(10, 3),
13802                    primary: None,
13803                },
13804            ],
13805            cx,
13806        );
13807        multibuffer.push_excerpts(
13808            buffer_3.clone(),
13809            [
13810                ExcerptRange {
13811                    context: Point::new(0, 0)..Point::new(3, 0),
13812                    primary: None,
13813                },
13814                ExcerptRange {
13815                    context: Point::new(5, 0)..Point::new(7, 0),
13816                    primary: None,
13817                },
13818                ExcerptRange {
13819                    context: Point::new(9, 0)..Point::new(10, 3),
13820                    primary: None,
13821                },
13822            ],
13823            cx,
13824        );
13825        multibuffer
13826    });
13827
13828    let editor = cx.add_window(|window, cx| {
13829        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13830    });
13831    editor
13832        .update(cx, |editor, _window, cx| {
13833            for (buffer, diff_base) in [
13834                (buffer_1.clone(), file_1_old),
13835                (buffer_2.clone(), file_2_old),
13836                (buffer_3.clone(), file_3_old),
13837            ] {
13838                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13839                editor
13840                    .buffer
13841                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13842            }
13843        })
13844        .unwrap();
13845
13846    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13847    cx.run_until_parked();
13848
13849    cx.assert_editor_state(
13850        &"
13851            ˇaaa
13852            ccc
13853            ddd
13854
13855            ggg
13856            hhh
13857
13858
13859            lll
13860            mmm
13861            NNN
13862
13863            qqq
13864            rrr
13865
13866            uuu
13867            111
13868            222
13869            333
13870
13871            666
13872            777
13873
13874            000
13875            !!!"
13876        .unindent(),
13877    );
13878
13879    cx.update_editor(|editor, window, cx| {
13880        editor.select_all(&SelectAll, window, cx);
13881        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13882    });
13883    cx.executor().run_until_parked();
13884
13885    cx.assert_state_with_diff(
13886        "
13887            «aaa
13888          - bbb
13889            ccc
13890            ddd
13891
13892            ggg
13893            hhh
13894
13895
13896            lll
13897            mmm
13898          - nnn
13899          + NNN
13900
13901            qqq
13902            rrr
13903
13904            uuu
13905            111
13906            222
13907            333
13908
13909          + 666
13910            777
13911
13912            000
13913            !!!ˇ»"
13914            .unindent(),
13915    );
13916}
13917
13918#[gpui::test]
13919async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13920    init_test(cx, |_| {});
13921
13922    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13923    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13924
13925    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13926    let multi_buffer = cx.new(|cx| {
13927        let mut multibuffer = MultiBuffer::new(ReadWrite);
13928        multibuffer.push_excerpts(
13929            buffer.clone(),
13930            [
13931                ExcerptRange {
13932                    context: Point::new(0, 0)..Point::new(2, 0),
13933                    primary: None,
13934                },
13935                ExcerptRange {
13936                    context: Point::new(4, 0)..Point::new(7, 0),
13937                    primary: None,
13938                },
13939                ExcerptRange {
13940                    context: Point::new(9, 0)..Point::new(10, 0),
13941                    primary: None,
13942                },
13943            ],
13944            cx,
13945        );
13946        multibuffer
13947    });
13948
13949    let editor = cx.add_window(|window, cx| {
13950        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13951    });
13952    editor
13953        .update(cx, |editor, _window, cx| {
13954            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13955            editor
13956                .buffer
13957                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13958        })
13959        .unwrap();
13960
13961    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13962    cx.run_until_parked();
13963
13964    cx.update_editor(|editor, window, cx| {
13965        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13966    });
13967    cx.executor().run_until_parked();
13968
13969    // When the start of a hunk coincides with the start of its excerpt,
13970    // the hunk is expanded. When the start of a a hunk is earlier than
13971    // the start of its excerpt, the hunk is not expanded.
13972    cx.assert_state_with_diff(
13973        "
13974            ˇaaa
13975          - bbb
13976          + BBB
13977
13978          - ddd
13979          - eee
13980          + DDD
13981          + EEE
13982            fff
13983
13984            iii
13985        "
13986        .unindent(),
13987    );
13988}
13989
13990#[gpui::test]
13991async fn test_edits_around_expanded_insertion_hunks(
13992    executor: BackgroundExecutor,
13993    cx: &mut TestAppContext,
13994) {
13995    init_test(cx, |_| {});
13996
13997    let mut cx = EditorTestContext::new(cx).await;
13998
13999    let diff_base = r#"
14000        use some::mod1;
14001        use some::mod2;
14002
14003        const A: u32 = 42;
14004
14005        fn main() {
14006            println!("hello");
14007
14008            println!("world");
14009        }
14010        "#
14011    .unindent();
14012    executor.run_until_parked();
14013    cx.set_state(
14014        &r#"
14015        use some::mod1;
14016        use some::mod2;
14017
14018        const A: u32 = 42;
14019        const B: u32 = 42;
14020        const C: u32 = 42;
14021        ˇ
14022
14023        fn main() {
14024            println!("hello");
14025
14026            println!("world");
14027        }
14028        "#
14029        .unindent(),
14030    );
14031
14032    cx.set_head_text(&diff_base);
14033    executor.run_until_parked();
14034
14035    cx.update_editor(|editor, window, cx| {
14036        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14037    });
14038    executor.run_until_parked();
14039
14040    cx.assert_state_with_diff(
14041        r#"
14042        use some::mod1;
14043        use some::mod2;
14044
14045        const A: u32 = 42;
14046      + const B: u32 = 42;
14047      + const C: u32 = 42;
14048      + ˇ
14049
14050        fn main() {
14051            println!("hello");
14052
14053            println!("world");
14054        }
14055      "#
14056        .unindent(),
14057    );
14058
14059    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14060    executor.run_until_parked();
14061
14062    cx.assert_state_with_diff(
14063        r#"
14064        use some::mod1;
14065        use some::mod2;
14066
14067        const A: u32 = 42;
14068      + const B: u32 = 42;
14069      + const C: u32 = 42;
14070      + const D: u32 = 42;
14071      + ˇ
14072
14073        fn main() {
14074            println!("hello");
14075
14076            println!("world");
14077        }
14078      "#
14079        .unindent(),
14080    );
14081
14082    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14083    executor.run_until_parked();
14084
14085    cx.assert_state_with_diff(
14086        r#"
14087        use some::mod1;
14088        use some::mod2;
14089
14090        const A: u32 = 42;
14091      + const B: u32 = 42;
14092      + const C: u32 = 42;
14093      + const D: u32 = 42;
14094      + const E: u32 = 42;
14095      + ˇ
14096
14097        fn main() {
14098            println!("hello");
14099
14100            println!("world");
14101        }
14102      "#
14103        .unindent(),
14104    );
14105
14106    cx.update_editor(|editor, window, cx| {
14107        editor.delete_line(&DeleteLine, window, cx);
14108    });
14109    executor.run_until_parked();
14110
14111    cx.assert_state_with_diff(
14112        r#"
14113        use some::mod1;
14114        use some::mod2;
14115
14116        const A: u32 = 42;
14117      + const B: u32 = 42;
14118      + const C: u32 = 42;
14119      + const D: u32 = 42;
14120      + const E: u32 = 42;
14121        ˇ
14122        fn main() {
14123            println!("hello");
14124
14125            println!("world");
14126        }
14127      "#
14128        .unindent(),
14129    );
14130
14131    cx.update_editor(|editor, window, cx| {
14132        editor.move_up(&MoveUp, window, cx);
14133        editor.delete_line(&DeleteLine, window, cx);
14134        editor.move_up(&MoveUp, window, cx);
14135        editor.delete_line(&DeleteLine, window, cx);
14136        editor.move_up(&MoveUp, window, cx);
14137        editor.delete_line(&DeleteLine, window, cx);
14138    });
14139    executor.run_until_parked();
14140    cx.assert_state_with_diff(
14141        r#"
14142        use some::mod1;
14143        use some::mod2;
14144
14145        const A: u32 = 42;
14146      + const B: u32 = 42;
14147        ˇ
14148        fn main() {
14149            println!("hello");
14150
14151            println!("world");
14152        }
14153      "#
14154        .unindent(),
14155    );
14156
14157    cx.update_editor(|editor, window, cx| {
14158        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14159        editor.delete_line(&DeleteLine, window, cx);
14160    });
14161    executor.run_until_parked();
14162    cx.assert_state_with_diff(
14163        r#"
14164        ˇ
14165        fn main() {
14166            println!("hello");
14167
14168            println!("world");
14169        }
14170      "#
14171        .unindent(),
14172    );
14173}
14174
14175#[gpui::test]
14176async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14177    init_test(cx, |_| {});
14178
14179    let mut cx = EditorTestContext::new(cx).await;
14180    cx.set_head_text(indoc! { "
14181        one
14182        two
14183        three
14184        four
14185        five
14186        "
14187    });
14188    cx.set_state(indoc! { "
14189        one
14190        ˇthree
14191        five
14192    "});
14193    cx.run_until_parked();
14194    cx.update_editor(|editor, window, cx| {
14195        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14196    });
14197    cx.assert_state_with_diff(
14198        indoc! { "
14199        one
14200      - two
14201        ˇthree
14202      - four
14203        five
14204    "}
14205        .to_string(),
14206    );
14207    cx.update_editor(|editor, window, cx| {
14208        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14209    });
14210
14211    cx.assert_state_with_diff(
14212        indoc! { "
14213        one
14214        ˇthree
14215        five
14216    "}
14217        .to_string(),
14218    );
14219
14220    cx.set_state(indoc! { "
14221        one
14222        ˇTWO
14223        three
14224        four
14225        five
14226    "});
14227    cx.run_until_parked();
14228    cx.update_editor(|editor, window, cx| {
14229        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14230    });
14231
14232    cx.assert_state_with_diff(
14233        indoc! { "
14234            one
14235          - two
14236          + ˇTWO
14237            three
14238            four
14239            five
14240        "}
14241        .to_string(),
14242    );
14243    cx.update_editor(|editor, window, cx| {
14244        editor.move_up(&Default::default(), window, cx);
14245        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14246    });
14247    cx.assert_state_with_diff(
14248        indoc! { "
14249            one
14250            ˇTWO
14251            three
14252            four
14253            five
14254        "}
14255        .to_string(),
14256    );
14257}
14258
14259#[gpui::test]
14260async fn test_edits_around_expanded_deletion_hunks(
14261    executor: BackgroundExecutor,
14262    cx: &mut TestAppContext,
14263) {
14264    init_test(cx, |_| {});
14265
14266    let mut cx = EditorTestContext::new(cx).await;
14267
14268    let diff_base = r#"
14269        use some::mod1;
14270        use some::mod2;
14271
14272        const A: u32 = 42;
14273        const B: u32 = 42;
14274        const C: u32 = 42;
14275
14276
14277        fn main() {
14278            println!("hello");
14279
14280            println!("world");
14281        }
14282    "#
14283    .unindent();
14284    executor.run_until_parked();
14285    cx.set_state(
14286        &r#"
14287        use some::mod1;
14288        use some::mod2;
14289
14290        ˇconst B: u32 = 42;
14291        const C: u32 = 42;
14292
14293
14294        fn main() {
14295            println!("hello");
14296
14297            println!("world");
14298        }
14299        "#
14300        .unindent(),
14301    );
14302
14303    cx.set_head_text(&diff_base);
14304    executor.run_until_parked();
14305
14306    cx.update_editor(|editor, window, cx| {
14307        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14308    });
14309    executor.run_until_parked();
14310
14311    cx.assert_state_with_diff(
14312        r#"
14313        use some::mod1;
14314        use some::mod2;
14315
14316      - const A: u32 = 42;
14317        ˇconst B: u32 = 42;
14318        const C: u32 = 42;
14319
14320
14321        fn main() {
14322            println!("hello");
14323
14324            println!("world");
14325        }
14326      "#
14327        .unindent(),
14328    );
14329
14330    cx.update_editor(|editor, window, cx| {
14331        editor.delete_line(&DeleteLine, window, cx);
14332    });
14333    executor.run_until_parked();
14334    cx.assert_state_with_diff(
14335        r#"
14336        use some::mod1;
14337        use some::mod2;
14338
14339      - const A: u32 = 42;
14340      - const B: u32 = 42;
14341        ˇconst C: u32 = 42;
14342
14343
14344        fn main() {
14345            println!("hello");
14346
14347            println!("world");
14348        }
14349      "#
14350        .unindent(),
14351    );
14352
14353    cx.update_editor(|editor, window, cx| {
14354        editor.delete_line(&DeleteLine, window, cx);
14355    });
14356    executor.run_until_parked();
14357    cx.assert_state_with_diff(
14358        r#"
14359        use some::mod1;
14360        use some::mod2;
14361
14362      - const A: u32 = 42;
14363      - const B: u32 = 42;
14364      - const C: u32 = 42;
14365        ˇ
14366
14367        fn main() {
14368            println!("hello");
14369
14370            println!("world");
14371        }
14372      "#
14373        .unindent(),
14374    );
14375
14376    cx.update_editor(|editor, window, cx| {
14377        editor.handle_input("replacement", window, cx);
14378    });
14379    executor.run_until_parked();
14380    cx.assert_state_with_diff(
14381        r#"
14382        use some::mod1;
14383        use some::mod2;
14384
14385      - const A: u32 = 42;
14386      - const B: u32 = 42;
14387      - const C: u32 = 42;
14388      -
14389      + replacementˇ
14390
14391        fn main() {
14392            println!("hello");
14393
14394            println!("world");
14395        }
14396      "#
14397        .unindent(),
14398    );
14399}
14400
14401#[gpui::test]
14402async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14403    init_test(cx, |_| {});
14404
14405    let mut cx = EditorTestContext::new(cx).await;
14406
14407    let base_text = r#"
14408        one
14409        two
14410        three
14411        four
14412        five
14413    "#
14414    .unindent();
14415    executor.run_until_parked();
14416    cx.set_state(
14417        &r#"
14418        one
14419        two
14420        fˇour
14421        five
14422        "#
14423        .unindent(),
14424    );
14425
14426    cx.set_head_text(&base_text);
14427    executor.run_until_parked();
14428
14429    cx.update_editor(|editor, window, cx| {
14430        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14431    });
14432    executor.run_until_parked();
14433
14434    cx.assert_state_with_diff(
14435        r#"
14436          one
14437          two
14438        - three
14439          fˇour
14440          five
14441        "#
14442        .unindent(),
14443    );
14444
14445    cx.update_editor(|editor, window, cx| {
14446        editor.backspace(&Backspace, window, cx);
14447        editor.backspace(&Backspace, window, cx);
14448    });
14449    executor.run_until_parked();
14450    cx.assert_state_with_diff(
14451        r#"
14452          one
14453          two
14454        - threeˇ
14455        - four
14456        + our
14457          five
14458        "#
14459        .unindent(),
14460    );
14461}
14462
14463#[gpui::test]
14464async fn test_edit_after_expanded_modification_hunk(
14465    executor: BackgroundExecutor,
14466    cx: &mut TestAppContext,
14467) {
14468    init_test(cx, |_| {});
14469
14470    let mut cx = EditorTestContext::new(cx).await;
14471
14472    let diff_base = r#"
14473        use some::mod1;
14474        use some::mod2;
14475
14476        const A: u32 = 42;
14477        const B: u32 = 42;
14478        const C: u32 = 42;
14479        const D: u32 = 42;
14480
14481
14482        fn main() {
14483            println!("hello");
14484
14485            println!("world");
14486        }"#
14487    .unindent();
14488
14489    cx.set_state(
14490        &r#"
14491        use some::mod1;
14492        use some::mod2;
14493
14494        const A: u32 = 42;
14495        const B: u32 = 42;
14496        const C: u32 = 43ˇ
14497        const D: u32 = 42;
14498
14499
14500        fn main() {
14501            println!("hello");
14502
14503            println!("world");
14504        }"#
14505        .unindent(),
14506    );
14507
14508    cx.set_head_text(&diff_base);
14509    executor.run_until_parked();
14510    cx.update_editor(|editor, window, cx| {
14511        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14512    });
14513    executor.run_until_parked();
14514
14515    cx.assert_state_with_diff(
14516        r#"
14517        use some::mod1;
14518        use some::mod2;
14519
14520        const A: u32 = 42;
14521        const B: u32 = 42;
14522      - const C: u32 = 42;
14523      + const C: u32 = 43ˇ
14524        const D: u32 = 42;
14525
14526
14527        fn main() {
14528            println!("hello");
14529
14530            println!("world");
14531        }"#
14532        .unindent(),
14533    );
14534
14535    cx.update_editor(|editor, window, cx| {
14536        editor.handle_input("\nnew_line\n", window, cx);
14537    });
14538    executor.run_until_parked();
14539
14540    cx.assert_state_with_diff(
14541        r#"
14542        use some::mod1;
14543        use some::mod2;
14544
14545        const A: u32 = 42;
14546        const B: u32 = 42;
14547      - const C: u32 = 42;
14548      + const C: u32 = 43
14549      + new_line
14550      + ˇ
14551        const D: u32 = 42;
14552
14553
14554        fn main() {
14555            println!("hello");
14556
14557            println!("world");
14558        }"#
14559        .unindent(),
14560    );
14561}
14562
14563#[gpui::test]
14564async fn test_stage_and_unstage_added_file_hunk(
14565    executor: BackgroundExecutor,
14566    cx: &mut TestAppContext,
14567) {
14568    init_test(cx, |_| {});
14569
14570    let mut cx = EditorTestContext::new(cx).await;
14571    cx.update_editor(|editor, _, cx| {
14572        editor.set_expand_all_diff_hunks(cx);
14573    });
14574
14575    let working_copy = r#"
14576            ˇfn main() {
14577                println!("hello, world!");
14578            }
14579        "#
14580    .unindent();
14581
14582    cx.set_state(&working_copy);
14583    executor.run_until_parked();
14584
14585    cx.assert_state_with_diff(
14586        r#"
14587            + ˇfn main() {
14588            +     println!("hello, world!");
14589            + }
14590        "#
14591        .unindent(),
14592    );
14593    cx.assert_index_text(None);
14594
14595    cx.update_editor(|editor, window, cx| {
14596        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14597    });
14598    executor.run_until_parked();
14599    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14600    cx.assert_state_with_diff(
14601        r#"
14602            + ˇfn main() {
14603            +     println!("hello, world!");
14604            + }
14605        "#
14606        .unindent(),
14607    );
14608
14609    cx.update_editor(|editor, window, cx| {
14610        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14611    });
14612    executor.run_until_parked();
14613    cx.assert_index_text(None);
14614}
14615
14616async fn setup_indent_guides_editor(
14617    text: &str,
14618    cx: &mut TestAppContext,
14619) -> (BufferId, EditorTestContext) {
14620    init_test(cx, |_| {});
14621
14622    let mut cx = EditorTestContext::new(cx).await;
14623
14624    let buffer_id = cx.update_editor(|editor, window, cx| {
14625        editor.set_text(text, window, cx);
14626        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14627
14628        buffer_ids[0]
14629    });
14630
14631    (buffer_id, cx)
14632}
14633
14634fn assert_indent_guides(
14635    range: Range<u32>,
14636    expected: Vec<IndentGuide>,
14637    active_indices: Option<Vec<usize>>,
14638    cx: &mut EditorTestContext,
14639) {
14640    let indent_guides = cx.update_editor(|editor, window, cx| {
14641        let snapshot = editor.snapshot(window, cx).display_snapshot;
14642        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14643            editor,
14644            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14645            true,
14646            &snapshot,
14647            cx,
14648        );
14649
14650        indent_guides.sort_by(|a, b| {
14651            a.depth.cmp(&b.depth).then(
14652                a.start_row
14653                    .cmp(&b.start_row)
14654                    .then(a.end_row.cmp(&b.end_row)),
14655            )
14656        });
14657        indent_guides
14658    });
14659
14660    if let Some(expected) = active_indices {
14661        let active_indices = cx.update_editor(|editor, window, cx| {
14662            let snapshot = editor.snapshot(window, cx).display_snapshot;
14663            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14664        });
14665
14666        assert_eq!(
14667            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14668            expected,
14669            "Active indent guide indices do not match"
14670        );
14671    }
14672
14673    assert_eq!(indent_guides, expected, "Indent guides do not match");
14674}
14675
14676fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14677    IndentGuide {
14678        buffer_id,
14679        start_row: MultiBufferRow(start_row),
14680        end_row: MultiBufferRow(end_row),
14681        depth,
14682        tab_size: 4,
14683        settings: IndentGuideSettings {
14684            enabled: true,
14685            line_width: 1,
14686            active_line_width: 1,
14687            ..Default::default()
14688        },
14689    }
14690}
14691
14692#[gpui::test]
14693async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14694    let (buffer_id, mut cx) = setup_indent_guides_editor(
14695        &"
14696    fn main() {
14697        let a = 1;
14698    }"
14699        .unindent(),
14700        cx,
14701    )
14702    .await;
14703
14704    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14705}
14706
14707#[gpui::test]
14708async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14709    let (buffer_id, mut cx) = setup_indent_guides_editor(
14710        &"
14711    fn main() {
14712        let a = 1;
14713        let b = 2;
14714    }"
14715        .unindent(),
14716        cx,
14717    )
14718    .await;
14719
14720    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14721}
14722
14723#[gpui::test]
14724async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14725    let (buffer_id, mut cx) = setup_indent_guides_editor(
14726        &"
14727    fn main() {
14728        let a = 1;
14729        if a == 3 {
14730            let b = 2;
14731        } else {
14732            let c = 3;
14733        }
14734    }"
14735        .unindent(),
14736        cx,
14737    )
14738    .await;
14739
14740    assert_indent_guides(
14741        0..8,
14742        vec![
14743            indent_guide(buffer_id, 1, 6, 0),
14744            indent_guide(buffer_id, 3, 3, 1),
14745            indent_guide(buffer_id, 5, 5, 1),
14746        ],
14747        None,
14748        &mut cx,
14749    );
14750}
14751
14752#[gpui::test]
14753async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14754    let (buffer_id, mut cx) = setup_indent_guides_editor(
14755        &"
14756    fn main() {
14757        let a = 1;
14758            let b = 2;
14759        let c = 3;
14760    }"
14761        .unindent(),
14762        cx,
14763    )
14764    .await;
14765
14766    assert_indent_guides(
14767        0..5,
14768        vec![
14769            indent_guide(buffer_id, 1, 3, 0),
14770            indent_guide(buffer_id, 2, 2, 1),
14771        ],
14772        None,
14773        &mut cx,
14774    );
14775}
14776
14777#[gpui::test]
14778async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14779    let (buffer_id, mut cx) = setup_indent_guides_editor(
14780        &"
14781        fn main() {
14782            let a = 1;
14783
14784            let c = 3;
14785        }"
14786        .unindent(),
14787        cx,
14788    )
14789    .await;
14790
14791    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14792}
14793
14794#[gpui::test]
14795async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14796    let (buffer_id, mut cx) = setup_indent_guides_editor(
14797        &"
14798        fn main() {
14799            let a = 1;
14800
14801            let c = 3;
14802
14803            if a == 3 {
14804                let b = 2;
14805            } else {
14806                let c = 3;
14807            }
14808        }"
14809        .unindent(),
14810        cx,
14811    )
14812    .await;
14813
14814    assert_indent_guides(
14815        0..11,
14816        vec![
14817            indent_guide(buffer_id, 1, 9, 0),
14818            indent_guide(buffer_id, 6, 6, 1),
14819            indent_guide(buffer_id, 8, 8, 1),
14820        ],
14821        None,
14822        &mut cx,
14823    );
14824}
14825
14826#[gpui::test]
14827async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14828    let (buffer_id, mut cx) = setup_indent_guides_editor(
14829        &"
14830        fn main() {
14831            let a = 1;
14832
14833            let c = 3;
14834
14835            if a == 3 {
14836                let b = 2;
14837            } else {
14838                let c = 3;
14839            }
14840        }"
14841        .unindent(),
14842        cx,
14843    )
14844    .await;
14845
14846    assert_indent_guides(
14847        1..11,
14848        vec![
14849            indent_guide(buffer_id, 1, 9, 0),
14850            indent_guide(buffer_id, 6, 6, 1),
14851            indent_guide(buffer_id, 8, 8, 1),
14852        ],
14853        None,
14854        &mut cx,
14855    );
14856}
14857
14858#[gpui::test]
14859async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14860    let (buffer_id, mut cx) = setup_indent_guides_editor(
14861        &"
14862        fn main() {
14863            let a = 1;
14864
14865            let c = 3;
14866
14867            if a == 3 {
14868                let b = 2;
14869            } else {
14870                let c = 3;
14871            }
14872        }"
14873        .unindent(),
14874        cx,
14875    )
14876    .await;
14877
14878    assert_indent_guides(
14879        1..10,
14880        vec![
14881            indent_guide(buffer_id, 1, 9, 0),
14882            indent_guide(buffer_id, 6, 6, 1),
14883            indent_guide(buffer_id, 8, 8, 1),
14884        ],
14885        None,
14886        &mut cx,
14887    );
14888}
14889
14890#[gpui::test]
14891async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14892    let (buffer_id, mut cx) = setup_indent_guides_editor(
14893        &"
14894        block1
14895            block2
14896                block3
14897                    block4
14898            block2
14899        block1
14900        block1"
14901            .unindent(),
14902        cx,
14903    )
14904    .await;
14905
14906    assert_indent_guides(
14907        1..10,
14908        vec![
14909            indent_guide(buffer_id, 1, 4, 0),
14910            indent_guide(buffer_id, 2, 3, 1),
14911            indent_guide(buffer_id, 3, 3, 2),
14912        ],
14913        None,
14914        &mut cx,
14915    );
14916}
14917
14918#[gpui::test]
14919async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14920    let (buffer_id, mut cx) = setup_indent_guides_editor(
14921        &"
14922        block1
14923            block2
14924                block3
14925
14926        block1
14927        block1"
14928            .unindent(),
14929        cx,
14930    )
14931    .await;
14932
14933    assert_indent_guides(
14934        0..6,
14935        vec![
14936            indent_guide(buffer_id, 1, 2, 0),
14937            indent_guide(buffer_id, 2, 2, 1),
14938        ],
14939        None,
14940        &mut cx,
14941    );
14942}
14943
14944#[gpui::test]
14945async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14946    let (buffer_id, mut cx) = setup_indent_guides_editor(
14947        &"
14948        block1
14949
14950
14951
14952            block2
14953        "
14954        .unindent(),
14955        cx,
14956    )
14957    .await;
14958
14959    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14960}
14961
14962#[gpui::test]
14963async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14964    let (buffer_id, mut cx) = setup_indent_guides_editor(
14965        &"
14966        def a:
14967        \tb = 3
14968        \tif True:
14969        \t\tc = 4
14970        \t\td = 5
14971        \tprint(b)
14972        "
14973        .unindent(),
14974        cx,
14975    )
14976    .await;
14977
14978    assert_indent_guides(
14979        0..6,
14980        vec![
14981            indent_guide(buffer_id, 1, 6, 0),
14982            indent_guide(buffer_id, 3, 4, 1),
14983        ],
14984        None,
14985        &mut cx,
14986    );
14987}
14988
14989#[gpui::test]
14990async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14991    let (buffer_id, mut cx) = setup_indent_guides_editor(
14992        &"
14993    fn main() {
14994        let a = 1;
14995    }"
14996        .unindent(),
14997        cx,
14998    )
14999    .await;
15000
15001    cx.update_editor(|editor, window, cx| {
15002        editor.change_selections(None, window, cx, |s| {
15003            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15004        });
15005    });
15006
15007    assert_indent_guides(
15008        0..3,
15009        vec![indent_guide(buffer_id, 1, 1, 0)],
15010        Some(vec![0]),
15011        &mut cx,
15012    );
15013}
15014
15015#[gpui::test]
15016async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15017    let (buffer_id, mut cx) = setup_indent_guides_editor(
15018        &"
15019    fn main() {
15020        if 1 == 2 {
15021            let a = 1;
15022        }
15023    }"
15024        .unindent(),
15025        cx,
15026    )
15027    .await;
15028
15029    cx.update_editor(|editor, window, cx| {
15030        editor.change_selections(None, window, cx, |s| {
15031            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15032        });
15033    });
15034
15035    assert_indent_guides(
15036        0..4,
15037        vec![
15038            indent_guide(buffer_id, 1, 3, 0),
15039            indent_guide(buffer_id, 2, 2, 1),
15040        ],
15041        Some(vec![1]),
15042        &mut cx,
15043    );
15044
15045    cx.update_editor(|editor, window, cx| {
15046        editor.change_selections(None, window, cx, |s| {
15047            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15048        });
15049    });
15050
15051    assert_indent_guides(
15052        0..4,
15053        vec![
15054            indent_guide(buffer_id, 1, 3, 0),
15055            indent_guide(buffer_id, 2, 2, 1),
15056        ],
15057        Some(vec![1]),
15058        &mut cx,
15059    );
15060
15061    cx.update_editor(|editor, window, cx| {
15062        editor.change_selections(None, window, cx, |s| {
15063            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15064        });
15065    });
15066
15067    assert_indent_guides(
15068        0..4,
15069        vec![
15070            indent_guide(buffer_id, 1, 3, 0),
15071            indent_guide(buffer_id, 2, 2, 1),
15072        ],
15073        Some(vec![0]),
15074        &mut cx,
15075    );
15076}
15077
15078#[gpui::test]
15079async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15080    let (buffer_id, mut cx) = setup_indent_guides_editor(
15081        &"
15082    fn main() {
15083        let a = 1;
15084
15085        let b = 2;
15086    }"
15087        .unindent(),
15088        cx,
15089    )
15090    .await;
15091
15092    cx.update_editor(|editor, window, cx| {
15093        editor.change_selections(None, window, cx, |s| {
15094            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15095        });
15096    });
15097
15098    assert_indent_guides(
15099        0..5,
15100        vec![indent_guide(buffer_id, 1, 3, 0)],
15101        Some(vec![0]),
15102        &mut cx,
15103    );
15104}
15105
15106#[gpui::test]
15107async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15108    let (buffer_id, mut cx) = setup_indent_guides_editor(
15109        &"
15110    def m:
15111        a = 1
15112        pass"
15113            .unindent(),
15114        cx,
15115    )
15116    .await;
15117
15118    cx.update_editor(|editor, window, cx| {
15119        editor.change_selections(None, window, cx, |s| {
15120            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15121        });
15122    });
15123
15124    assert_indent_guides(
15125        0..3,
15126        vec![indent_guide(buffer_id, 1, 2, 0)],
15127        Some(vec![0]),
15128        &mut cx,
15129    );
15130}
15131
15132#[gpui::test]
15133async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15134    init_test(cx, |_| {});
15135    let mut cx = EditorTestContext::new(cx).await;
15136    let text = indoc! {
15137        "
15138        impl A {
15139            fn b() {
15140                0;
15141                3;
15142                5;
15143                6;
15144                7;
15145            }
15146        }
15147        "
15148    };
15149    let base_text = indoc! {
15150        "
15151        impl A {
15152            fn b() {
15153                0;
15154                1;
15155                2;
15156                3;
15157                4;
15158            }
15159            fn c() {
15160                5;
15161                6;
15162                7;
15163            }
15164        }
15165        "
15166    };
15167
15168    cx.update_editor(|editor, window, cx| {
15169        editor.set_text(text, window, cx);
15170
15171        editor.buffer().update(cx, |multibuffer, cx| {
15172            let buffer = multibuffer.as_singleton().unwrap();
15173            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15174
15175            multibuffer.set_all_diff_hunks_expanded(cx);
15176            multibuffer.add_diff(diff, cx);
15177
15178            buffer.read(cx).remote_id()
15179        })
15180    });
15181    cx.run_until_parked();
15182
15183    cx.assert_state_with_diff(
15184        indoc! { "
15185          impl A {
15186              fn b() {
15187                  0;
15188        -         1;
15189        -         2;
15190                  3;
15191        -         4;
15192        -     }
15193        -     fn c() {
15194                  5;
15195                  6;
15196                  7;
15197              }
15198          }
15199          ˇ"
15200        }
15201        .to_string(),
15202    );
15203
15204    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15205        editor
15206            .snapshot(window, cx)
15207            .buffer_snapshot
15208            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15209            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15210            .collect::<Vec<_>>()
15211    });
15212    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15213    assert_eq!(
15214        actual_guides,
15215        vec![
15216            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15217            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15218            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15219        ]
15220    );
15221}
15222
15223#[gpui::test]
15224async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15225    init_test(cx, |_| {});
15226    let mut cx = EditorTestContext::new(cx).await;
15227
15228    let diff_base = r#"
15229        a
15230        b
15231        c
15232        "#
15233    .unindent();
15234
15235    cx.set_state(
15236        &r#"
15237        ˇA
15238        b
15239        C
15240        "#
15241        .unindent(),
15242    );
15243    cx.set_head_text(&diff_base);
15244    cx.update_editor(|editor, window, cx| {
15245        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15246    });
15247    executor.run_until_parked();
15248
15249    let both_hunks_expanded = r#"
15250        - a
15251        + ˇA
15252          b
15253        - c
15254        + C
15255        "#
15256    .unindent();
15257
15258    cx.assert_state_with_diff(both_hunks_expanded.clone());
15259
15260    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15261        let snapshot = editor.snapshot(window, cx);
15262        let hunks = editor
15263            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15264            .collect::<Vec<_>>();
15265        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15266        let buffer_id = hunks[0].buffer_id;
15267        hunks
15268            .into_iter()
15269            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15270            .collect::<Vec<_>>()
15271    });
15272    assert_eq!(hunk_ranges.len(), 2);
15273
15274    cx.update_editor(|editor, _, cx| {
15275        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15276    });
15277    executor.run_until_parked();
15278
15279    let second_hunk_expanded = r#"
15280          ˇA
15281          b
15282        - c
15283        + C
15284        "#
15285    .unindent();
15286
15287    cx.assert_state_with_diff(second_hunk_expanded);
15288
15289    cx.update_editor(|editor, _, cx| {
15290        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15291    });
15292    executor.run_until_parked();
15293
15294    cx.assert_state_with_diff(both_hunks_expanded.clone());
15295
15296    cx.update_editor(|editor, _, cx| {
15297        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15298    });
15299    executor.run_until_parked();
15300
15301    let first_hunk_expanded = r#"
15302        - a
15303        + ˇA
15304          b
15305          C
15306        "#
15307    .unindent();
15308
15309    cx.assert_state_with_diff(first_hunk_expanded);
15310
15311    cx.update_editor(|editor, _, cx| {
15312        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15313    });
15314    executor.run_until_parked();
15315
15316    cx.assert_state_with_diff(both_hunks_expanded);
15317
15318    cx.set_state(
15319        &r#"
15320        ˇA
15321        b
15322        "#
15323        .unindent(),
15324    );
15325    cx.run_until_parked();
15326
15327    // TODO this cursor position seems bad
15328    cx.assert_state_with_diff(
15329        r#"
15330        - ˇa
15331        + A
15332          b
15333        "#
15334        .unindent(),
15335    );
15336
15337    cx.update_editor(|editor, window, cx| {
15338        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15339    });
15340
15341    cx.assert_state_with_diff(
15342        r#"
15343            - ˇa
15344            + A
15345              b
15346            - c
15347            "#
15348        .unindent(),
15349    );
15350
15351    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15352        let snapshot = editor.snapshot(window, cx);
15353        let hunks = editor
15354            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15355            .collect::<Vec<_>>();
15356        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15357        let buffer_id = hunks[0].buffer_id;
15358        hunks
15359            .into_iter()
15360            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15361            .collect::<Vec<_>>()
15362    });
15363    assert_eq!(hunk_ranges.len(), 2);
15364
15365    cx.update_editor(|editor, _, cx| {
15366        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15367    });
15368    executor.run_until_parked();
15369
15370    cx.assert_state_with_diff(
15371        r#"
15372        - ˇa
15373        + A
15374          b
15375        "#
15376        .unindent(),
15377    );
15378}
15379
15380#[gpui::test]
15381async fn test_toggle_deletion_hunk_at_start_of_file(
15382    executor: BackgroundExecutor,
15383    cx: &mut TestAppContext,
15384) {
15385    init_test(cx, |_| {});
15386    let mut cx = EditorTestContext::new(cx).await;
15387
15388    let diff_base = r#"
15389        a
15390        b
15391        c
15392        "#
15393    .unindent();
15394
15395    cx.set_state(
15396        &r#"
15397        ˇb
15398        c
15399        "#
15400        .unindent(),
15401    );
15402    cx.set_head_text(&diff_base);
15403    cx.update_editor(|editor, window, cx| {
15404        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15405    });
15406    executor.run_until_parked();
15407
15408    let hunk_expanded = r#"
15409        - a
15410          ˇb
15411          c
15412        "#
15413    .unindent();
15414
15415    cx.assert_state_with_diff(hunk_expanded.clone());
15416
15417    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15418        let snapshot = editor.snapshot(window, cx);
15419        let hunks = editor
15420            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15421            .collect::<Vec<_>>();
15422        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15423        let buffer_id = hunks[0].buffer_id;
15424        hunks
15425            .into_iter()
15426            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15427            .collect::<Vec<_>>()
15428    });
15429    assert_eq!(hunk_ranges.len(), 1);
15430
15431    cx.update_editor(|editor, _, cx| {
15432        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15433    });
15434    executor.run_until_parked();
15435
15436    let hunk_collapsed = r#"
15437          ˇb
15438          c
15439        "#
15440    .unindent();
15441
15442    cx.assert_state_with_diff(hunk_collapsed);
15443
15444    cx.update_editor(|editor, _, cx| {
15445        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15446    });
15447    executor.run_until_parked();
15448
15449    cx.assert_state_with_diff(hunk_expanded.clone());
15450}
15451
15452#[gpui::test]
15453async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15454    init_test(cx, |_| {});
15455
15456    let fs = FakeFs::new(cx.executor());
15457    fs.insert_tree(
15458        path!("/test"),
15459        json!({
15460            ".git": {},
15461            "file-1": "ONE\n",
15462            "file-2": "TWO\n",
15463            "file-3": "THREE\n",
15464        }),
15465    )
15466    .await;
15467
15468    fs.set_head_for_repo(
15469        path!("/test/.git").as_ref(),
15470        &[
15471            ("file-1".into(), "one\n".into()),
15472            ("file-2".into(), "two\n".into()),
15473            ("file-3".into(), "three\n".into()),
15474        ],
15475    );
15476
15477    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15478    let mut buffers = vec![];
15479    for i in 1..=3 {
15480        let buffer = project
15481            .update(cx, |project, cx| {
15482                let path = format!(path!("/test/file-{}"), i);
15483                project.open_local_buffer(path, cx)
15484            })
15485            .await
15486            .unwrap();
15487        buffers.push(buffer);
15488    }
15489
15490    let multibuffer = cx.new(|cx| {
15491        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15492        multibuffer.set_all_diff_hunks_expanded(cx);
15493        for buffer in &buffers {
15494            let snapshot = buffer.read(cx).snapshot();
15495            multibuffer.set_excerpts_for_path(
15496                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15497                buffer.clone(),
15498                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15499                DEFAULT_MULTIBUFFER_CONTEXT,
15500                cx,
15501            );
15502        }
15503        multibuffer
15504    });
15505
15506    let editor = cx.add_window(|window, cx| {
15507        Editor::new(
15508            EditorMode::Full,
15509            multibuffer,
15510            Some(project),
15511            true,
15512            window,
15513            cx,
15514        )
15515    });
15516    cx.run_until_parked();
15517
15518    let snapshot = editor
15519        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15520        .unwrap();
15521    let hunks = snapshot
15522        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15523        .map(|hunk| match hunk {
15524            DisplayDiffHunk::Unfolded {
15525                display_row_range, ..
15526            } => display_row_range,
15527            DisplayDiffHunk::Folded { .. } => unreachable!(),
15528        })
15529        .collect::<Vec<_>>();
15530    assert_eq!(
15531        hunks,
15532        [
15533            DisplayRow(3)..DisplayRow(5),
15534            DisplayRow(10)..DisplayRow(12),
15535            DisplayRow(17)..DisplayRow(19),
15536        ]
15537    );
15538}
15539
15540#[gpui::test]
15541async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15542    init_test(cx, |_| {});
15543
15544    let mut cx = EditorTestContext::new(cx).await;
15545    cx.set_head_text(indoc! { "
15546        one
15547        two
15548        three
15549        four
15550        five
15551        "
15552    });
15553    cx.set_index_text(indoc! { "
15554        one
15555        two
15556        three
15557        four
15558        five
15559        "
15560    });
15561    cx.set_state(indoc! {"
15562        one
15563        TWO
15564        ˇTHREE
15565        FOUR
15566        five
15567    "});
15568    cx.run_until_parked();
15569    cx.update_editor(|editor, window, cx| {
15570        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15571    });
15572    cx.run_until_parked();
15573    cx.assert_index_text(Some(indoc! {"
15574        one
15575        TWO
15576        THREE
15577        FOUR
15578        five
15579    "}));
15580    cx.set_state(indoc! { "
15581        one
15582        TWO
15583        ˇTHREE-HUNDRED
15584        FOUR
15585        five
15586    "});
15587    cx.run_until_parked();
15588    cx.update_editor(|editor, window, cx| {
15589        let snapshot = editor.snapshot(window, cx);
15590        let hunks = editor
15591            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15592            .collect::<Vec<_>>();
15593        assert_eq!(hunks.len(), 1);
15594        assert_eq!(
15595            hunks[0].status(),
15596            DiffHunkStatus {
15597                kind: DiffHunkStatusKind::Modified,
15598                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15599            }
15600        );
15601
15602        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15603    });
15604    cx.run_until_parked();
15605    cx.assert_index_text(Some(indoc! {"
15606        one
15607        TWO
15608        THREE-HUNDRED
15609        FOUR
15610        five
15611    "}));
15612}
15613
15614#[gpui::test]
15615fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15616    init_test(cx, |_| {});
15617
15618    let editor = cx.add_window(|window, cx| {
15619        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15620        build_editor(buffer, window, cx)
15621    });
15622
15623    let render_args = Arc::new(Mutex::new(None));
15624    let snapshot = editor
15625        .update(cx, |editor, window, cx| {
15626            let snapshot = editor.buffer().read(cx).snapshot(cx);
15627            let range =
15628                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15629
15630            struct RenderArgs {
15631                row: MultiBufferRow,
15632                folded: bool,
15633                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15634            }
15635
15636            let crease = Crease::inline(
15637                range,
15638                FoldPlaceholder::test(),
15639                {
15640                    let toggle_callback = render_args.clone();
15641                    move |row, folded, callback, _window, _cx| {
15642                        *toggle_callback.lock() = Some(RenderArgs {
15643                            row,
15644                            folded,
15645                            callback,
15646                        });
15647                        div()
15648                    }
15649                },
15650                |_row, _folded, _window, _cx| div(),
15651            );
15652
15653            editor.insert_creases(Some(crease), cx);
15654            let snapshot = editor.snapshot(window, cx);
15655            let _div = snapshot.render_crease_toggle(
15656                MultiBufferRow(1),
15657                false,
15658                cx.entity().clone(),
15659                window,
15660                cx,
15661            );
15662            snapshot
15663        })
15664        .unwrap();
15665
15666    let render_args = render_args.lock().take().unwrap();
15667    assert_eq!(render_args.row, MultiBufferRow(1));
15668    assert!(!render_args.folded);
15669    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15670
15671    cx.update_window(*editor, |_, window, cx| {
15672        (render_args.callback)(true, window, cx)
15673    })
15674    .unwrap();
15675    let snapshot = editor
15676        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15677        .unwrap();
15678    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15679
15680    cx.update_window(*editor, |_, window, cx| {
15681        (render_args.callback)(false, window, cx)
15682    })
15683    .unwrap();
15684    let snapshot = editor
15685        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15686        .unwrap();
15687    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15688}
15689
15690#[gpui::test]
15691async fn test_input_text(cx: &mut TestAppContext) {
15692    init_test(cx, |_| {});
15693    let mut cx = EditorTestContext::new(cx).await;
15694
15695    cx.set_state(
15696        &r#"ˇone
15697        two
15698
15699        three
15700        fourˇ
15701        five
15702
15703        siˇx"#
15704            .unindent(),
15705    );
15706
15707    cx.dispatch_action(HandleInput(String::new()));
15708    cx.assert_editor_state(
15709        &r#"ˇone
15710        two
15711
15712        three
15713        fourˇ
15714        five
15715
15716        siˇx"#
15717            .unindent(),
15718    );
15719
15720    cx.dispatch_action(HandleInput("AAAA".to_string()));
15721    cx.assert_editor_state(
15722        &r#"AAAAˇone
15723        two
15724
15725        three
15726        fourAAAAˇ
15727        five
15728
15729        siAAAAˇx"#
15730            .unindent(),
15731    );
15732}
15733
15734#[gpui::test]
15735async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15736    init_test(cx, |_| {});
15737
15738    let mut cx = EditorTestContext::new(cx).await;
15739    cx.set_state(
15740        r#"let foo = 1;
15741let foo = 2;
15742let foo = 3;
15743let fooˇ = 4;
15744let foo = 5;
15745let foo = 6;
15746let foo = 7;
15747let foo = 8;
15748let foo = 9;
15749let foo = 10;
15750let foo = 11;
15751let foo = 12;
15752let foo = 13;
15753let foo = 14;
15754let foo = 15;"#,
15755    );
15756
15757    cx.update_editor(|e, window, cx| {
15758        assert_eq!(
15759            e.next_scroll_position,
15760            NextScrollCursorCenterTopBottom::Center,
15761            "Default next scroll direction is center",
15762        );
15763
15764        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15765        assert_eq!(
15766            e.next_scroll_position,
15767            NextScrollCursorCenterTopBottom::Top,
15768            "After center, next scroll direction should be top",
15769        );
15770
15771        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15772        assert_eq!(
15773            e.next_scroll_position,
15774            NextScrollCursorCenterTopBottom::Bottom,
15775            "After top, next scroll direction should be bottom",
15776        );
15777
15778        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15779        assert_eq!(
15780            e.next_scroll_position,
15781            NextScrollCursorCenterTopBottom::Center,
15782            "After bottom, scrolling should start over",
15783        );
15784
15785        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15786        assert_eq!(
15787            e.next_scroll_position,
15788            NextScrollCursorCenterTopBottom::Top,
15789            "Scrolling continues if retriggered fast enough"
15790        );
15791    });
15792
15793    cx.executor()
15794        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15795    cx.executor().run_until_parked();
15796    cx.update_editor(|e, _, _| {
15797        assert_eq!(
15798            e.next_scroll_position,
15799            NextScrollCursorCenterTopBottom::Center,
15800            "If scrolling is not triggered fast enough, it should reset"
15801        );
15802    });
15803}
15804
15805#[gpui::test]
15806async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15807    init_test(cx, |_| {});
15808    let mut cx = EditorLspTestContext::new_rust(
15809        lsp::ServerCapabilities {
15810            definition_provider: Some(lsp::OneOf::Left(true)),
15811            references_provider: Some(lsp::OneOf::Left(true)),
15812            ..lsp::ServerCapabilities::default()
15813        },
15814        cx,
15815    )
15816    .await;
15817
15818    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15819        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15820            move |params, _| async move {
15821                if empty_go_to_definition {
15822                    Ok(None)
15823                } else {
15824                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15825                        uri: params.text_document_position_params.text_document.uri,
15826                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15827                    })))
15828                }
15829            },
15830        );
15831        let references =
15832            cx.lsp
15833                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15834                    Ok(Some(vec![lsp::Location {
15835                        uri: params.text_document_position.text_document.uri,
15836                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15837                    }]))
15838                });
15839        (go_to_definition, references)
15840    };
15841
15842    cx.set_state(
15843        &r#"fn one() {
15844            let mut a = ˇtwo();
15845        }
15846
15847        fn two() {}"#
15848            .unindent(),
15849    );
15850    set_up_lsp_handlers(false, &mut cx);
15851    let navigated = cx
15852        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15853        .await
15854        .expect("Failed to navigate to definition");
15855    assert_eq!(
15856        navigated,
15857        Navigated::Yes,
15858        "Should have navigated to definition from the GetDefinition response"
15859    );
15860    cx.assert_editor_state(
15861        &r#"fn one() {
15862            let mut a = two();
15863        }
15864
15865        fn «twoˇ»() {}"#
15866            .unindent(),
15867    );
15868
15869    let editors = cx.update_workspace(|workspace, _, cx| {
15870        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15871    });
15872    cx.update_editor(|_, _, test_editor_cx| {
15873        assert_eq!(
15874            editors.len(),
15875            1,
15876            "Initially, only one, test, editor should be open in the workspace"
15877        );
15878        assert_eq!(
15879            test_editor_cx.entity(),
15880            editors.last().expect("Asserted len is 1").clone()
15881        );
15882    });
15883
15884    set_up_lsp_handlers(true, &mut cx);
15885    let navigated = cx
15886        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15887        .await
15888        .expect("Failed to navigate to lookup references");
15889    assert_eq!(
15890        navigated,
15891        Navigated::Yes,
15892        "Should have navigated to references as a fallback after empty GoToDefinition response"
15893    );
15894    // We should not change the selections in the existing file,
15895    // if opening another milti buffer with the references
15896    cx.assert_editor_state(
15897        &r#"fn one() {
15898            let mut a = two();
15899        }
15900
15901        fn «twoˇ»() {}"#
15902            .unindent(),
15903    );
15904    let editors = cx.update_workspace(|workspace, _, cx| {
15905        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15906    });
15907    cx.update_editor(|_, _, test_editor_cx| {
15908        assert_eq!(
15909            editors.len(),
15910            2,
15911            "After falling back to references search, we open a new editor with the results"
15912        );
15913        let references_fallback_text = editors
15914            .into_iter()
15915            .find(|new_editor| *new_editor != test_editor_cx.entity())
15916            .expect("Should have one non-test editor now")
15917            .read(test_editor_cx)
15918            .text(test_editor_cx);
15919        assert_eq!(
15920            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15921            "Should use the range from the references response and not the GoToDefinition one"
15922        );
15923    });
15924}
15925
15926#[gpui::test]
15927async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15928    init_test(cx, |_| {});
15929
15930    let language = Arc::new(Language::new(
15931        LanguageConfig::default(),
15932        Some(tree_sitter_rust::LANGUAGE.into()),
15933    ));
15934
15935    let text = r#"
15936        #[cfg(test)]
15937        mod tests() {
15938            #[test]
15939            fn runnable_1() {
15940                let a = 1;
15941            }
15942
15943            #[test]
15944            fn runnable_2() {
15945                let a = 1;
15946                let b = 2;
15947            }
15948        }
15949    "#
15950    .unindent();
15951
15952    let fs = FakeFs::new(cx.executor());
15953    fs.insert_file("/file.rs", Default::default()).await;
15954
15955    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15956    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15957    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15958    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15959    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15960
15961    let editor = cx.new_window_entity(|window, cx| {
15962        Editor::new(
15963            EditorMode::Full,
15964            multi_buffer,
15965            Some(project.clone()),
15966            true,
15967            window,
15968            cx,
15969        )
15970    });
15971
15972    editor.update_in(cx, |editor, window, cx| {
15973        let snapshot = editor.buffer().read(cx).snapshot(cx);
15974        editor.tasks.insert(
15975            (buffer.read(cx).remote_id(), 3),
15976            RunnableTasks {
15977                templates: vec![],
15978                offset: snapshot.anchor_before(43),
15979                column: 0,
15980                extra_variables: HashMap::default(),
15981                context_range: BufferOffset(43)..BufferOffset(85),
15982            },
15983        );
15984        editor.tasks.insert(
15985            (buffer.read(cx).remote_id(), 8),
15986            RunnableTasks {
15987                templates: vec![],
15988                offset: snapshot.anchor_before(86),
15989                column: 0,
15990                extra_variables: HashMap::default(),
15991                context_range: BufferOffset(86)..BufferOffset(191),
15992            },
15993        );
15994
15995        // Test finding task when cursor is inside function body
15996        editor.change_selections(None, window, cx, |s| {
15997            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15998        });
15999        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16000        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16001
16002        // Test finding task when cursor is on function name
16003        editor.change_selections(None, window, cx, |s| {
16004            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16005        });
16006        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16007        assert_eq!(row, 8, "Should find task when cursor is on function name");
16008    });
16009}
16010
16011#[gpui::test]
16012async fn test_folding_buffers(cx: &mut TestAppContext) {
16013    init_test(cx, |_| {});
16014
16015    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16016    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16017    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16018
16019    let fs = FakeFs::new(cx.executor());
16020    fs.insert_tree(
16021        path!("/a"),
16022        json!({
16023            "first.rs": sample_text_1,
16024            "second.rs": sample_text_2,
16025            "third.rs": sample_text_3,
16026        }),
16027    )
16028    .await;
16029    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16030    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16031    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16032    let worktree = project.update(cx, |project, cx| {
16033        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16034        assert_eq!(worktrees.len(), 1);
16035        worktrees.pop().unwrap()
16036    });
16037    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16038
16039    let buffer_1 = project
16040        .update(cx, |project, cx| {
16041            project.open_buffer((worktree_id, "first.rs"), cx)
16042        })
16043        .await
16044        .unwrap();
16045    let buffer_2 = project
16046        .update(cx, |project, cx| {
16047            project.open_buffer((worktree_id, "second.rs"), cx)
16048        })
16049        .await
16050        .unwrap();
16051    let buffer_3 = project
16052        .update(cx, |project, cx| {
16053            project.open_buffer((worktree_id, "third.rs"), cx)
16054        })
16055        .await
16056        .unwrap();
16057
16058    let multi_buffer = cx.new(|cx| {
16059        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16060        multi_buffer.push_excerpts(
16061            buffer_1.clone(),
16062            [
16063                ExcerptRange {
16064                    context: Point::new(0, 0)..Point::new(3, 0),
16065                    primary: None,
16066                },
16067                ExcerptRange {
16068                    context: Point::new(5, 0)..Point::new(7, 0),
16069                    primary: None,
16070                },
16071                ExcerptRange {
16072                    context: Point::new(9, 0)..Point::new(10, 4),
16073                    primary: None,
16074                },
16075            ],
16076            cx,
16077        );
16078        multi_buffer.push_excerpts(
16079            buffer_2.clone(),
16080            [
16081                ExcerptRange {
16082                    context: Point::new(0, 0)..Point::new(3, 0),
16083                    primary: None,
16084                },
16085                ExcerptRange {
16086                    context: Point::new(5, 0)..Point::new(7, 0),
16087                    primary: None,
16088                },
16089                ExcerptRange {
16090                    context: Point::new(9, 0)..Point::new(10, 4),
16091                    primary: None,
16092                },
16093            ],
16094            cx,
16095        );
16096        multi_buffer.push_excerpts(
16097            buffer_3.clone(),
16098            [
16099                ExcerptRange {
16100                    context: Point::new(0, 0)..Point::new(3, 0),
16101                    primary: None,
16102                },
16103                ExcerptRange {
16104                    context: Point::new(5, 0)..Point::new(7, 0),
16105                    primary: None,
16106                },
16107                ExcerptRange {
16108                    context: Point::new(9, 0)..Point::new(10, 4),
16109                    primary: None,
16110                },
16111            ],
16112            cx,
16113        );
16114        multi_buffer
16115    });
16116    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16117        Editor::new(
16118            EditorMode::Full,
16119            multi_buffer.clone(),
16120            Some(project.clone()),
16121            true,
16122            window,
16123            cx,
16124        )
16125    });
16126
16127    assert_eq!(
16128        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16129        "\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",
16130    );
16131
16132    multi_buffer_editor.update(cx, |editor, cx| {
16133        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16134    });
16135    assert_eq!(
16136        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16137        "\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",
16138        "After folding the first buffer, its text should not be displayed"
16139    );
16140
16141    multi_buffer_editor.update(cx, |editor, cx| {
16142        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16143    });
16144    assert_eq!(
16145        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16146        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
16147        "After folding the second buffer, its text should not be displayed"
16148    );
16149
16150    multi_buffer_editor.update(cx, |editor, cx| {
16151        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16152    });
16153    assert_eq!(
16154        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16155        "\n\n\n\n\n",
16156        "After folding the third buffer, its text should not be displayed"
16157    );
16158
16159    // Emulate selection inside the fold logic, that should work
16160    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16161        editor
16162            .snapshot(window, cx)
16163            .next_line_boundary(Point::new(0, 4));
16164    });
16165
16166    multi_buffer_editor.update(cx, |editor, cx| {
16167        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16168    });
16169    assert_eq!(
16170        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16171        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
16172        "After unfolding the second buffer, its text should be displayed"
16173    );
16174
16175    // Typing inside of buffer 1 causes that buffer to be unfolded.
16176    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16177        assert_eq!(
16178            multi_buffer
16179                .read(cx)
16180                .snapshot(cx)
16181                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16182                .collect::<String>(),
16183            "bbbb"
16184        );
16185        editor.change_selections(None, window, cx, |selections| {
16186            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16187        });
16188        editor.handle_input("B", window, cx);
16189    });
16190
16191    assert_eq!(
16192        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16193        "\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",
16194        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16195    );
16196
16197    multi_buffer_editor.update(cx, |editor, cx| {
16198        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16199    });
16200    assert_eq!(
16201        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16202        "\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",
16203        "After unfolding the all buffers, all original text should be displayed"
16204    );
16205}
16206
16207#[gpui::test]
16208async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16209    init_test(cx, |_| {});
16210
16211    let sample_text_1 = "1111\n2222\n3333".to_string();
16212    let sample_text_2 = "4444\n5555\n6666".to_string();
16213    let sample_text_3 = "7777\n8888\n9999".to_string();
16214
16215    let fs = FakeFs::new(cx.executor());
16216    fs.insert_tree(
16217        path!("/a"),
16218        json!({
16219            "first.rs": sample_text_1,
16220            "second.rs": sample_text_2,
16221            "third.rs": sample_text_3,
16222        }),
16223    )
16224    .await;
16225    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16226    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16227    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16228    let worktree = project.update(cx, |project, cx| {
16229        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16230        assert_eq!(worktrees.len(), 1);
16231        worktrees.pop().unwrap()
16232    });
16233    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16234
16235    let buffer_1 = project
16236        .update(cx, |project, cx| {
16237            project.open_buffer((worktree_id, "first.rs"), cx)
16238        })
16239        .await
16240        .unwrap();
16241    let buffer_2 = project
16242        .update(cx, |project, cx| {
16243            project.open_buffer((worktree_id, "second.rs"), cx)
16244        })
16245        .await
16246        .unwrap();
16247    let buffer_3 = project
16248        .update(cx, |project, cx| {
16249            project.open_buffer((worktree_id, "third.rs"), cx)
16250        })
16251        .await
16252        .unwrap();
16253
16254    let multi_buffer = cx.new(|cx| {
16255        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16256        multi_buffer.push_excerpts(
16257            buffer_1.clone(),
16258            [ExcerptRange {
16259                context: Point::new(0, 0)..Point::new(3, 0),
16260                primary: None,
16261            }],
16262            cx,
16263        );
16264        multi_buffer.push_excerpts(
16265            buffer_2.clone(),
16266            [ExcerptRange {
16267                context: Point::new(0, 0)..Point::new(3, 0),
16268                primary: None,
16269            }],
16270            cx,
16271        );
16272        multi_buffer.push_excerpts(
16273            buffer_3.clone(),
16274            [ExcerptRange {
16275                context: Point::new(0, 0)..Point::new(3, 0),
16276                primary: None,
16277            }],
16278            cx,
16279        );
16280        multi_buffer
16281    });
16282
16283    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16284        Editor::new(
16285            EditorMode::Full,
16286            multi_buffer,
16287            Some(project.clone()),
16288            true,
16289            window,
16290            cx,
16291        )
16292    });
16293
16294    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
16295    assert_eq!(
16296        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16297        full_text,
16298    );
16299
16300    multi_buffer_editor.update(cx, |editor, cx| {
16301        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16302    });
16303    assert_eq!(
16304        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16305        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
16306        "After folding the first buffer, its text should not be displayed"
16307    );
16308
16309    multi_buffer_editor.update(cx, |editor, cx| {
16310        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16311    });
16312
16313    assert_eq!(
16314        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16315        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
16316        "After folding the second buffer, its text should not be displayed"
16317    );
16318
16319    multi_buffer_editor.update(cx, |editor, cx| {
16320        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16321    });
16322    assert_eq!(
16323        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16324        "\n\n\n\n\n",
16325        "After folding the third buffer, its text should not be displayed"
16326    );
16327
16328    multi_buffer_editor.update(cx, |editor, cx| {
16329        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16330    });
16331    assert_eq!(
16332        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16333        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
16334        "After unfolding the second buffer, its text should be displayed"
16335    );
16336
16337    multi_buffer_editor.update(cx, |editor, cx| {
16338        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16339    });
16340    assert_eq!(
16341        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16342        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
16343        "After unfolding the first buffer, its text should be displayed"
16344    );
16345
16346    multi_buffer_editor.update(cx, |editor, cx| {
16347        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16348    });
16349    assert_eq!(
16350        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16351        full_text,
16352        "After unfolding all buffers, all original text should be displayed"
16353    );
16354}
16355
16356#[gpui::test]
16357async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16358    init_test(cx, |_| {});
16359
16360    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16361
16362    let fs = FakeFs::new(cx.executor());
16363    fs.insert_tree(
16364        path!("/a"),
16365        json!({
16366            "main.rs": sample_text,
16367        }),
16368    )
16369    .await;
16370    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16371    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16372    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16373    let worktree = project.update(cx, |project, cx| {
16374        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16375        assert_eq!(worktrees.len(), 1);
16376        worktrees.pop().unwrap()
16377    });
16378    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16379
16380    let buffer_1 = project
16381        .update(cx, |project, cx| {
16382            project.open_buffer((worktree_id, "main.rs"), cx)
16383        })
16384        .await
16385        .unwrap();
16386
16387    let multi_buffer = cx.new(|cx| {
16388        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16389        multi_buffer.push_excerpts(
16390            buffer_1.clone(),
16391            [ExcerptRange {
16392                context: Point::new(0, 0)
16393                    ..Point::new(
16394                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16395                        0,
16396                    ),
16397                primary: None,
16398            }],
16399            cx,
16400        );
16401        multi_buffer
16402    });
16403    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16404        Editor::new(
16405            EditorMode::Full,
16406            multi_buffer,
16407            Some(project.clone()),
16408            true,
16409            window,
16410            cx,
16411        )
16412    });
16413
16414    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16415    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16416        enum TestHighlight {}
16417        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16418        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16419        editor.highlight_text::<TestHighlight>(
16420            vec![highlight_range.clone()],
16421            HighlightStyle::color(Hsla::green()),
16422            cx,
16423        );
16424        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16425    });
16426
16427    let full_text = format!("\n\n\n{sample_text}\n");
16428    assert_eq!(
16429        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16430        full_text,
16431    );
16432}
16433
16434#[gpui::test]
16435async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16436    init_test(cx, |_| {});
16437    cx.update(|cx| {
16438        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16439            "keymaps/default-linux.json",
16440            cx,
16441        )
16442        .unwrap();
16443        cx.bind_keys(default_key_bindings);
16444    });
16445
16446    let (editor, cx) = cx.add_window_view(|window, cx| {
16447        let multi_buffer = MultiBuffer::build_multi(
16448            [
16449                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16450                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16451                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16452                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16453            ],
16454            cx,
16455        );
16456        let mut editor = Editor::new(
16457            EditorMode::Full,
16458            multi_buffer.clone(),
16459            None,
16460            true,
16461            window,
16462            cx,
16463        );
16464
16465        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16466        // fold all but the second buffer, so that we test navigating between two
16467        // adjacent folded buffers, as well as folded buffers at the start and
16468        // end the multibuffer
16469        editor.fold_buffer(buffer_ids[0], cx);
16470        editor.fold_buffer(buffer_ids[2], cx);
16471        editor.fold_buffer(buffer_ids[3], cx);
16472
16473        editor
16474    });
16475    cx.simulate_resize(size(px(1000.), px(1000.)));
16476
16477    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16478    cx.assert_excerpts_with_selections(indoc! {"
16479        [EXCERPT]
16480        ˇ[FOLDED]
16481        [EXCERPT]
16482        a1
16483        b1
16484        [EXCERPT]
16485        [FOLDED]
16486        [EXCERPT]
16487        [FOLDED]
16488        "
16489    });
16490    cx.simulate_keystroke("down");
16491    cx.assert_excerpts_with_selections(indoc! {"
16492        [EXCERPT]
16493        [FOLDED]
16494        [EXCERPT]
16495        ˇa1
16496        b1
16497        [EXCERPT]
16498        [FOLDED]
16499        [EXCERPT]
16500        [FOLDED]
16501        "
16502    });
16503    cx.simulate_keystroke("down");
16504    cx.assert_excerpts_with_selections(indoc! {"
16505        [EXCERPT]
16506        [FOLDED]
16507        [EXCERPT]
16508        a1
16509        ˇb1
16510        [EXCERPT]
16511        [FOLDED]
16512        [EXCERPT]
16513        [FOLDED]
16514        "
16515    });
16516    cx.simulate_keystroke("down");
16517    cx.assert_excerpts_with_selections(indoc! {"
16518        [EXCERPT]
16519        [FOLDED]
16520        [EXCERPT]
16521        a1
16522        b1
16523        ˇ[EXCERPT]
16524        [FOLDED]
16525        [EXCERPT]
16526        [FOLDED]
16527        "
16528    });
16529    cx.simulate_keystroke("down");
16530    cx.assert_excerpts_with_selections(indoc! {"
16531        [EXCERPT]
16532        [FOLDED]
16533        [EXCERPT]
16534        a1
16535        b1
16536        [EXCERPT]
16537        ˇ[FOLDED]
16538        [EXCERPT]
16539        [FOLDED]
16540        "
16541    });
16542    for _ in 0..5 {
16543        cx.simulate_keystroke("down");
16544        cx.assert_excerpts_with_selections(indoc! {"
16545            [EXCERPT]
16546            [FOLDED]
16547            [EXCERPT]
16548            a1
16549            b1
16550            [EXCERPT]
16551            [FOLDED]
16552            [EXCERPT]
16553            ˇ[FOLDED]
16554            "
16555        });
16556    }
16557
16558    cx.simulate_keystroke("up");
16559    cx.assert_excerpts_with_selections(indoc! {"
16560        [EXCERPT]
16561        [FOLDED]
16562        [EXCERPT]
16563        a1
16564        b1
16565        [EXCERPT]
16566        ˇ[FOLDED]
16567        [EXCERPT]
16568        [FOLDED]
16569        "
16570    });
16571    cx.simulate_keystroke("up");
16572    cx.assert_excerpts_with_selections(indoc! {"
16573        [EXCERPT]
16574        [FOLDED]
16575        [EXCERPT]
16576        a1
16577        b1
16578        ˇ[EXCERPT]
16579        [FOLDED]
16580        [EXCERPT]
16581        [FOLDED]
16582        "
16583    });
16584    cx.simulate_keystroke("up");
16585    cx.assert_excerpts_with_selections(indoc! {"
16586        [EXCERPT]
16587        [FOLDED]
16588        [EXCERPT]
16589        a1
16590        ˇb1
16591        [EXCERPT]
16592        [FOLDED]
16593        [EXCERPT]
16594        [FOLDED]
16595        "
16596    });
16597    cx.simulate_keystroke("up");
16598    cx.assert_excerpts_with_selections(indoc! {"
16599        [EXCERPT]
16600        [FOLDED]
16601        [EXCERPT]
16602        ˇa1
16603        b1
16604        [EXCERPT]
16605        [FOLDED]
16606        [EXCERPT]
16607        [FOLDED]
16608        "
16609    });
16610    for _ in 0..5 {
16611        cx.simulate_keystroke("up");
16612        cx.assert_excerpts_with_selections(indoc! {"
16613            [EXCERPT]
16614            ˇ[FOLDED]
16615            [EXCERPT]
16616            a1
16617            b1
16618            [EXCERPT]
16619            [FOLDED]
16620            [EXCERPT]
16621            [FOLDED]
16622            "
16623        });
16624    }
16625}
16626
16627#[gpui::test]
16628async fn test_inline_completion_text(cx: &mut TestAppContext) {
16629    init_test(cx, |_| {});
16630
16631    // Simple insertion
16632    assert_highlighted_edits(
16633        "Hello, world!",
16634        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16635        true,
16636        cx,
16637        |highlighted_edits, cx| {
16638            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16639            assert_eq!(highlighted_edits.highlights.len(), 1);
16640            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16641            assert_eq!(
16642                highlighted_edits.highlights[0].1.background_color,
16643                Some(cx.theme().status().created_background)
16644            );
16645        },
16646    )
16647    .await;
16648
16649    // Replacement
16650    assert_highlighted_edits(
16651        "This is a test.",
16652        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16653        false,
16654        cx,
16655        |highlighted_edits, cx| {
16656            assert_eq!(highlighted_edits.text, "That is a test.");
16657            assert_eq!(highlighted_edits.highlights.len(), 1);
16658            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16659            assert_eq!(
16660                highlighted_edits.highlights[0].1.background_color,
16661                Some(cx.theme().status().created_background)
16662            );
16663        },
16664    )
16665    .await;
16666
16667    // Multiple edits
16668    assert_highlighted_edits(
16669        "Hello, world!",
16670        vec![
16671            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16672            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16673        ],
16674        false,
16675        cx,
16676        |highlighted_edits, cx| {
16677            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16678            assert_eq!(highlighted_edits.highlights.len(), 2);
16679            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16680            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16681            assert_eq!(
16682                highlighted_edits.highlights[0].1.background_color,
16683                Some(cx.theme().status().created_background)
16684            );
16685            assert_eq!(
16686                highlighted_edits.highlights[1].1.background_color,
16687                Some(cx.theme().status().created_background)
16688            );
16689        },
16690    )
16691    .await;
16692
16693    // Multiple lines with edits
16694    assert_highlighted_edits(
16695        "First line\nSecond line\nThird line\nFourth line",
16696        vec![
16697            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16698            (
16699                Point::new(2, 0)..Point::new(2, 10),
16700                "New third line".to_string(),
16701            ),
16702            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16703        ],
16704        false,
16705        cx,
16706        |highlighted_edits, cx| {
16707            assert_eq!(
16708                highlighted_edits.text,
16709                "Second modified\nNew third line\nFourth updated line"
16710            );
16711            assert_eq!(highlighted_edits.highlights.len(), 3);
16712            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16713            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16714            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16715            for highlight in &highlighted_edits.highlights {
16716                assert_eq!(
16717                    highlight.1.background_color,
16718                    Some(cx.theme().status().created_background)
16719                );
16720            }
16721        },
16722    )
16723    .await;
16724}
16725
16726#[gpui::test]
16727async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16728    init_test(cx, |_| {});
16729
16730    // Deletion
16731    assert_highlighted_edits(
16732        "Hello, world!",
16733        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16734        true,
16735        cx,
16736        |highlighted_edits, cx| {
16737            assert_eq!(highlighted_edits.text, "Hello, world!");
16738            assert_eq!(highlighted_edits.highlights.len(), 1);
16739            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16740            assert_eq!(
16741                highlighted_edits.highlights[0].1.background_color,
16742                Some(cx.theme().status().deleted_background)
16743            );
16744        },
16745    )
16746    .await;
16747
16748    // Insertion
16749    assert_highlighted_edits(
16750        "Hello, world!",
16751        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16752        true,
16753        cx,
16754        |highlighted_edits, cx| {
16755            assert_eq!(highlighted_edits.highlights.len(), 1);
16756            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16757            assert_eq!(
16758                highlighted_edits.highlights[0].1.background_color,
16759                Some(cx.theme().status().created_background)
16760            );
16761        },
16762    )
16763    .await;
16764}
16765
16766async fn assert_highlighted_edits(
16767    text: &str,
16768    edits: Vec<(Range<Point>, String)>,
16769    include_deletions: bool,
16770    cx: &mut TestAppContext,
16771    assertion_fn: impl Fn(HighlightedText, &App),
16772) {
16773    let window = cx.add_window(|window, cx| {
16774        let buffer = MultiBuffer::build_simple(text, cx);
16775        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16776    });
16777    let cx = &mut VisualTestContext::from_window(*window, cx);
16778
16779    let (buffer, snapshot) = window
16780        .update(cx, |editor, _window, cx| {
16781            (
16782                editor.buffer().clone(),
16783                editor.buffer().read(cx).snapshot(cx),
16784            )
16785        })
16786        .unwrap();
16787
16788    let edits = edits
16789        .into_iter()
16790        .map(|(range, edit)| {
16791            (
16792                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16793                edit,
16794            )
16795        })
16796        .collect::<Vec<_>>();
16797
16798    let text_anchor_edits = edits
16799        .clone()
16800        .into_iter()
16801        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16802        .collect::<Vec<_>>();
16803
16804    let edit_preview = window
16805        .update(cx, |_, _window, cx| {
16806            buffer
16807                .read(cx)
16808                .as_singleton()
16809                .unwrap()
16810                .read(cx)
16811                .preview_edits(text_anchor_edits.into(), cx)
16812        })
16813        .unwrap()
16814        .await;
16815
16816    cx.update(|_window, cx| {
16817        let highlighted_edits = inline_completion_edit_text(
16818            &snapshot.as_singleton().unwrap().2,
16819            &edits,
16820            &edit_preview,
16821            include_deletions,
16822            cx,
16823        );
16824        assertion_fn(highlighted_edits, cx)
16825    });
16826}
16827
16828#[gpui::test]
16829async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16830    init_test(cx, |_| {});
16831    let capabilities = lsp::ServerCapabilities {
16832        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16833            prepare_provider: Some(true),
16834            work_done_progress_options: Default::default(),
16835        })),
16836        ..Default::default()
16837    };
16838    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16839
16840    cx.set_state(indoc! {"
16841        struct Fˇoo {}
16842    "});
16843
16844    cx.update_editor(|editor, _, cx| {
16845        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16846        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16847        editor.highlight_background::<DocumentHighlightRead>(
16848            &[highlight_range],
16849            |c| c.editor_document_highlight_read_background,
16850            cx,
16851        );
16852    });
16853
16854    let mut prepare_rename_handler =
16855        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16856            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16857                start: lsp::Position {
16858                    line: 0,
16859                    character: 7,
16860                },
16861                end: lsp::Position {
16862                    line: 0,
16863                    character: 10,
16864                },
16865            })))
16866        });
16867    let prepare_rename_task = cx
16868        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16869        .expect("Prepare rename was not started");
16870    prepare_rename_handler.next().await.unwrap();
16871    prepare_rename_task.await.expect("Prepare rename failed");
16872
16873    let mut rename_handler =
16874        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16875            let edit = lsp::TextEdit {
16876                range: lsp::Range {
16877                    start: lsp::Position {
16878                        line: 0,
16879                        character: 7,
16880                    },
16881                    end: lsp::Position {
16882                        line: 0,
16883                        character: 10,
16884                    },
16885                },
16886                new_text: "FooRenamed".to_string(),
16887            };
16888            Ok(Some(lsp::WorkspaceEdit::new(
16889                // Specify the same edit twice
16890                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16891            )))
16892        });
16893    let rename_task = cx
16894        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16895        .expect("Confirm rename was not started");
16896    rename_handler.next().await.unwrap();
16897    rename_task.await.expect("Confirm rename failed");
16898    cx.run_until_parked();
16899
16900    // Despite two edits, only one is actually applied as those are identical
16901    cx.assert_editor_state(indoc! {"
16902        struct FooRenamedˇ {}
16903    "});
16904}
16905
16906#[gpui::test]
16907async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16908    init_test(cx, |_| {});
16909    // These capabilities indicate that the server does not support prepare rename.
16910    let capabilities = lsp::ServerCapabilities {
16911        rename_provider: Some(lsp::OneOf::Left(true)),
16912        ..Default::default()
16913    };
16914    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16915
16916    cx.set_state(indoc! {"
16917        struct Fˇoo {}
16918    "});
16919
16920    cx.update_editor(|editor, _window, cx| {
16921        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16922        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16923        editor.highlight_background::<DocumentHighlightRead>(
16924            &[highlight_range],
16925            |c| c.editor_document_highlight_read_background,
16926            cx,
16927        );
16928    });
16929
16930    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16931        .expect("Prepare rename was not started")
16932        .await
16933        .expect("Prepare rename failed");
16934
16935    let mut rename_handler =
16936        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16937            let edit = lsp::TextEdit {
16938                range: lsp::Range {
16939                    start: lsp::Position {
16940                        line: 0,
16941                        character: 7,
16942                    },
16943                    end: lsp::Position {
16944                        line: 0,
16945                        character: 10,
16946                    },
16947                },
16948                new_text: "FooRenamed".to_string(),
16949            };
16950            Ok(Some(lsp::WorkspaceEdit::new(
16951                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16952            )))
16953        });
16954    let rename_task = cx
16955        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16956        .expect("Confirm rename was not started");
16957    rename_handler.next().await.unwrap();
16958    rename_task.await.expect("Confirm rename failed");
16959    cx.run_until_parked();
16960
16961    // Correct range is renamed, as `surrounding_word` is used to find it.
16962    cx.assert_editor_state(indoc! {"
16963        struct FooRenamedˇ {}
16964    "});
16965}
16966
16967#[gpui::test]
16968async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16969    init_test(cx, |_| {});
16970    let mut cx = EditorTestContext::new(cx).await;
16971
16972    let language = Arc::new(
16973        Language::new(
16974            LanguageConfig::default(),
16975            Some(tree_sitter_html::LANGUAGE.into()),
16976        )
16977        .with_brackets_query(
16978            r#"
16979            ("<" @open "/>" @close)
16980            ("</" @open ">" @close)
16981            ("<" @open ">" @close)
16982            ("\"" @open "\"" @close)
16983            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16984        "#,
16985        )
16986        .unwrap(),
16987    );
16988    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16989
16990    cx.set_state(indoc! {"
16991        <span>ˇ</span>
16992    "});
16993    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16994    cx.assert_editor_state(indoc! {"
16995        <span>
16996        ˇ
16997        </span>
16998    "});
16999
17000    cx.set_state(indoc! {"
17001        <span><span></span>ˇ</span>
17002    "});
17003    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17004    cx.assert_editor_state(indoc! {"
17005        <span><span></span>
17006        ˇ</span>
17007    "});
17008
17009    cx.set_state(indoc! {"
17010        <span>ˇ
17011        </span>
17012    "});
17013    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17014    cx.assert_editor_state(indoc! {"
17015        <span>
17016        ˇ
17017        </span>
17018    "});
17019}
17020
17021mod autoclose_tags {
17022    use super::*;
17023    use language::language_settings::JsxTagAutoCloseSettings;
17024    use languages::language;
17025
17026    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17027        init_test(cx, |settings| {
17028            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17029        });
17030
17031        let mut cx = EditorTestContext::new(cx).await;
17032        cx.update_buffer(|buffer, cx| {
17033            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17034
17035            buffer.set_language(Some(language), cx)
17036        });
17037
17038        cx
17039    }
17040
17041    macro_rules! check {
17042        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17043            #[gpui::test]
17044            async fn $name(cx: &mut TestAppContext) {
17045                let mut cx = test_setup(cx).await;
17046                cx.set_state($initial);
17047                cx.run_until_parked();
17048
17049                cx.update_editor(|editor, window, cx| {
17050                    editor.handle_input($input, window, cx);
17051                });
17052                cx.run_until_parked();
17053                cx.assert_editor_state($expected);
17054            }
17055        };
17056    }
17057
17058    check!(
17059        test_basic,
17060        "<divˇ" + ">" => "<div>ˇ</div>"
17061    );
17062
17063    check!(
17064        test_basic_nested,
17065        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17066    );
17067
17068    check!(
17069        test_basic_ignore_already_closed,
17070        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17071    );
17072
17073    check!(
17074        test_doesnt_autoclose_closing_tag,
17075        "</divˇ" + ">" => "</div>ˇ"
17076    );
17077
17078    check!(
17079        test_jsx_attr,
17080        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17081    );
17082
17083    check!(
17084        test_ignores_closing_tags_in_expr_block,
17085        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17086    );
17087
17088    check!(
17089        test_doesnt_autoclose_on_gt_in_expr,
17090        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17091    );
17092
17093    check!(
17094        test_ignores_closing_tags_with_different_tag_names,
17095        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17096    );
17097
17098    check!(
17099        test_autocloses_in_jsx_expression,
17100        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17101    );
17102
17103    check!(
17104        test_doesnt_autoclose_already_closed_in_jsx_expression,
17105        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17106    );
17107
17108    check!(
17109        test_autocloses_fragment,
17110        "" + ">" => "<>ˇ</>"
17111    );
17112
17113    check!(
17114        test_does_not_include_type_argument_in_autoclose_tag_name,
17115        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17116    );
17117
17118    check!(
17119        test_does_not_autoclose_doctype,
17120        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17121    );
17122
17123    check!(
17124        test_does_not_autoclose_comment,
17125        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17126    );
17127
17128    check!(
17129        test_multi_cursor_autoclose_same_tag,
17130        r#"
17131        <divˇ
17132        <divˇ
17133        "#
17134        + ">" =>
17135        r#"
17136        <div>ˇ</div>
17137        <div>ˇ</div>
17138        "#
17139    );
17140
17141    check!(
17142        test_multi_cursor_autoclose_different_tags,
17143        r#"
17144        <divˇ
17145        <spanˇ
17146        "#
17147        + ">" =>
17148        r#"
17149        <div>ˇ</div>
17150        <span>ˇ</span>
17151        "#
17152    );
17153
17154    check!(
17155        test_multi_cursor_autoclose_some_dont_autoclose_others,
17156        r#"
17157        <divˇ
17158        <div /ˇ
17159        <spanˇ</span>
17160        <!DOCTYPE htmlˇ
17161        </headˇ
17162        <Component<T>ˇ
17163        ˇ
17164        "#
17165        + ">" =>
17166        r#"
17167        <div>ˇ</div>
17168        <div />ˇ
17169        <span>ˇ</span>
17170        <!DOCTYPE html>ˇ
17171        </head>ˇ
17172        <Component<T>>ˇ</Component>
1717317174        "#
17175    );
17176
17177    check!(
17178        test_doesnt_mess_up_trailing_text,
17179        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17180    );
17181
17182    #[gpui::test]
17183    async fn test_multibuffer(cx: &mut TestAppContext) {
17184        init_test(cx, |settings| {
17185            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17186        });
17187
17188        let buffer_a = cx.new(|cx| {
17189            let mut buf = language::Buffer::local("<div", cx);
17190            buf.set_language(
17191                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17192                cx,
17193            );
17194            buf
17195        });
17196        let buffer_b = cx.new(|cx| {
17197            let mut buf = language::Buffer::local("<pre", cx);
17198            buf.set_language(
17199                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17200                cx,
17201            );
17202            buf
17203        });
17204        let buffer_c = cx.new(|cx| {
17205            let buf = language::Buffer::local("<span", cx);
17206            buf
17207        });
17208        let buffer = cx.new(|cx| {
17209            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17210            buf.push_excerpts(
17211                buffer_a,
17212                [ExcerptRange {
17213                    context: text::Anchor::MIN..text::Anchor::MAX,
17214                    primary: None,
17215                }],
17216                cx,
17217            );
17218            buf.push_excerpts(
17219                buffer_b,
17220                [ExcerptRange {
17221                    context: text::Anchor::MIN..text::Anchor::MAX,
17222                    primary: None,
17223                }],
17224                cx,
17225            );
17226            buf.push_excerpts(
17227                buffer_c,
17228                [ExcerptRange {
17229                    context: text::Anchor::MIN..text::Anchor::MAX,
17230                    primary: None,
17231                }],
17232                cx,
17233            );
17234            buf
17235        });
17236        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17237
17238        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17239
17240        cx.update_editor(|editor, window, cx| {
17241            editor.change_selections(None, window, cx, |selections| {
17242                selections.select(vec![
17243                    Selection::from_offset(4),
17244                    Selection::from_offset(9),
17245                    Selection::from_offset(15),
17246                ])
17247            })
17248        });
17249        cx.run_until_parked();
17250
17251        cx.update_editor(|editor, window, cx| {
17252            editor.handle_input(">", window, cx);
17253        });
17254        cx.run_until_parked();
17255
17256        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17257    }
17258}
17259
17260fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17261    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17262    point..point
17263}
17264
17265fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17266    let (text, ranges) = marked_text_ranges(marked_text, true);
17267    assert_eq!(editor.text(cx), text);
17268    assert_eq!(
17269        editor.selections.ranges(cx),
17270        ranges,
17271        "Assert selections are {}",
17272        marked_text
17273    );
17274}
17275
17276pub fn handle_signature_help_request(
17277    cx: &mut EditorLspTestContext,
17278    mocked_response: lsp::SignatureHelp,
17279) -> impl Future<Output = ()> {
17280    let mut request =
17281        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17282            let mocked_response = mocked_response.clone();
17283            async move { Ok(Some(mocked_response)) }
17284        });
17285
17286    async move {
17287        request.next().await;
17288    }
17289}
17290
17291/// Handle completion request passing a marked string specifying where the completion
17292/// should be triggered from using '|' character, what range should be replaced, and what completions
17293/// should be returned using '<' and '>' to delimit the range
17294pub fn handle_completion_request(
17295    cx: &mut EditorLspTestContext,
17296    marked_string: &str,
17297    completions: Vec<&'static str>,
17298    counter: Arc<AtomicUsize>,
17299) -> impl Future<Output = ()> {
17300    let complete_from_marker: TextRangeMarker = '|'.into();
17301    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17302    let (_, mut marked_ranges) = marked_text_ranges_by(
17303        marked_string,
17304        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17305    );
17306
17307    let complete_from_position =
17308        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17309    let replace_range =
17310        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17311
17312    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17313        let completions = completions.clone();
17314        counter.fetch_add(1, atomic::Ordering::Release);
17315        async move {
17316            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17317            assert_eq!(
17318                params.text_document_position.position,
17319                complete_from_position
17320            );
17321            Ok(Some(lsp::CompletionResponse::Array(
17322                completions
17323                    .iter()
17324                    .map(|completion_text| lsp::CompletionItem {
17325                        label: completion_text.to_string(),
17326                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17327                            range: replace_range,
17328                            new_text: completion_text.to_string(),
17329                        })),
17330                        ..Default::default()
17331                    })
17332                    .collect(),
17333            )))
17334        }
17335    });
17336
17337    async move {
17338        request.next().await;
17339    }
17340}
17341
17342fn handle_resolve_completion_request(
17343    cx: &mut EditorLspTestContext,
17344    edits: Option<Vec<(&'static str, &'static str)>>,
17345) -> impl Future<Output = ()> {
17346    let edits = edits.map(|edits| {
17347        edits
17348            .iter()
17349            .map(|(marked_string, new_text)| {
17350                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17351                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17352                lsp::TextEdit::new(replace_range, new_text.to_string())
17353            })
17354            .collect::<Vec<_>>()
17355    });
17356
17357    let mut request =
17358        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17359            let edits = edits.clone();
17360            async move {
17361                Ok(lsp::CompletionItem {
17362                    additional_text_edits: edits,
17363                    ..Default::default()
17364                })
17365            }
17366        });
17367
17368    async move {
17369        request.next().await;
17370    }
17371}
17372
17373pub(crate) fn update_test_language_settings(
17374    cx: &mut TestAppContext,
17375    f: impl Fn(&mut AllLanguageSettingsContent),
17376) {
17377    cx.update(|cx| {
17378        SettingsStore::update_global(cx, |store, cx| {
17379            store.update_user_settings::<AllLanguageSettings>(cx, f);
17380        });
17381    });
17382}
17383
17384pub(crate) fn update_test_project_settings(
17385    cx: &mut TestAppContext,
17386    f: impl Fn(&mut ProjectSettings),
17387) {
17388    cx.update(|cx| {
17389        SettingsStore::update_global(cx, |store, cx| {
17390            store.update_user_settings::<ProjectSettings>(cx, f);
17391        });
17392    });
17393}
17394
17395pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17396    cx.update(|cx| {
17397        assets::Assets.load_test_fonts(cx);
17398        let store = SettingsStore::test(cx);
17399        cx.set_global(store);
17400        theme::init(theme::LoadThemes::JustBase, cx);
17401        release_channel::init(SemanticVersion::default(), cx);
17402        client::init_settings(cx);
17403        language::init(cx);
17404        Project::init_settings(cx);
17405        workspace::init_settings(cx);
17406        crate::init(cx);
17407    });
17408
17409    update_test_language_settings(cx, f);
17410}
17411
17412#[track_caller]
17413fn assert_hunk_revert(
17414    not_reverted_text_with_selections: &str,
17415    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17416    expected_reverted_text_with_selections: &str,
17417    base_text: &str,
17418    cx: &mut EditorLspTestContext,
17419) {
17420    cx.set_state(not_reverted_text_with_selections);
17421    cx.set_head_text(base_text);
17422    cx.executor().run_until_parked();
17423
17424    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17425        let snapshot = editor.snapshot(window, cx);
17426        let reverted_hunk_statuses = snapshot
17427            .buffer_snapshot
17428            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17429            .map(|hunk| hunk.status().kind)
17430            .collect::<Vec<_>>();
17431
17432        editor.git_restore(&Default::default(), window, cx);
17433        reverted_hunk_statuses
17434    });
17435    cx.executor().run_until_parked();
17436    cx.assert_editor_state(expected_reverted_text_with_selections);
17437    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17438}