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_clipboard(cx: &mut TestAppContext) {
 4742    init_test(cx, |_| {});
 4743
 4744    let mut cx = EditorTestContext::new(cx).await;
 4745
 4746    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4747    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4748    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4749
 4750    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4751    cx.set_state("two ˇfour ˇsix ˇ");
 4752    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4753    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4754
 4755    // Paste again but with only two cursors. Since the number of cursors doesn't
 4756    // match the number of slices in the clipboard, the entire clipboard text
 4757    // is pasted at each cursor.
 4758    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4759    cx.update_editor(|e, window, cx| {
 4760        e.handle_input("( ", window, cx);
 4761        e.paste(&Paste, window, cx);
 4762        e.handle_input(") ", window, cx);
 4763    });
 4764    cx.assert_editor_state(
 4765        &([
 4766            "( one✅ ",
 4767            "three ",
 4768            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4769            "three ",
 4770            "five ) ˇ",
 4771        ]
 4772        .join("\n")),
 4773    );
 4774
 4775    // Cut with three selections, one of which is full-line.
 4776    cx.set_state(indoc! {"
 4777        1«2ˇ»3
 4778        4ˇ567
 4779        «8ˇ»9"});
 4780    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4781    cx.assert_editor_state(indoc! {"
 4782        1ˇ3
 4783        ˇ9"});
 4784
 4785    // Paste with three selections, noticing how the copied selection that was full-line
 4786    // gets inserted before the second cursor.
 4787    cx.set_state(indoc! {"
 4788        1ˇ3
 4789 4790        «oˇ»ne"});
 4791    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4792    cx.assert_editor_state(indoc! {"
 4793        12ˇ3
 4794        4567
 4795 4796        8ˇne"});
 4797
 4798    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4799    cx.set_state(indoc! {"
 4800        The quick brown
 4801        fox juˇmps over
 4802        the lazy dog"});
 4803    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4804    assert_eq!(
 4805        cx.read_from_clipboard()
 4806            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4807        Some("fox jumps over\n".to_string())
 4808    );
 4809
 4810    // Paste with three selections, noticing how the copied full-line selection is inserted
 4811    // before the empty selections but replaces the selection that is non-empty.
 4812    cx.set_state(indoc! {"
 4813        Tˇhe quick brown
 4814        «foˇ»x jumps over
 4815        tˇhe lazy dog"});
 4816    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4817    cx.assert_editor_state(indoc! {"
 4818        fox jumps over
 4819        Tˇhe quick brown
 4820        fox jumps over
 4821        ˇx jumps over
 4822        fox jumps over
 4823        tˇhe lazy dog"});
 4824}
 4825
 4826#[gpui::test]
 4827async fn test_paste_multiline(cx: &mut TestAppContext) {
 4828    init_test(cx, |_| {});
 4829
 4830    let mut cx = EditorTestContext::new(cx).await;
 4831    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4832
 4833    // Cut an indented block, without the leading whitespace.
 4834    cx.set_state(indoc! {"
 4835        const a: B = (
 4836            c(),
 4837            «d(
 4838                e,
 4839                f
 4840            )ˇ»
 4841        );
 4842    "});
 4843    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4844    cx.assert_editor_state(indoc! {"
 4845        const a: B = (
 4846            c(),
 4847            ˇ
 4848        );
 4849    "});
 4850
 4851    // Paste it at the same position.
 4852    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4853    cx.assert_editor_state(indoc! {"
 4854        const a: B = (
 4855            c(),
 4856            d(
 4857                e,
 4858                f
 4859 4860        );
 4861    "});
 4862
 4863    // Paste it at a line with a lower indent level.
 4864    cx.set_state(indoc! {"
 4865        ˇ
 4866        const a: B = (
 4867            c(),
 4868        );
 4869    "});
 4870    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4871    cx.assert_editor_state(indoc! {"
 4872        d(
 4873            e,
 4874            f
 4875 4876        const a: B = (
 4877            c(),
 4878        );
 4879    "});
 4880
 4881    // Cut an indented block, with the leading whitespace.
 4882    cx.set_state(indoc! {"
 4883        const a: B = (
 4884            c(),
 4885        «    d(
 4886                e,
 4887                f
 4888            )
 4889        ˇ»);
 4890    "});
 4891    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4892    cx.assert_editor_state(indoc! {"
 4893        const a: B = (
 4894            c(),
 4895        ˇ);
 4896    "});
 4897
 4898    // Paste it at the same position.
 4899    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4900    cx.assert_editor_state(indoc! {"
 4901        const a: B = (
 4902            c(),
 4903            d(
 4904                e,
 4905                f
 4906            )
 4907        ˇ);
 4908    "});
 4909
 4910    // Paste it at a line with a higher indent level.
 4911    cx.set_state(indoc! {"
 4912        const a: B = (
 4913            c(),
 4914            d(
 4915                e,
 4916 4917            )
 4918        );
 4919    "});
 4920    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4921    cx.assert_editor_state(indoc! {"
 4922        const a: B = (
 4923            c(),
 4924            d(
 4925                e,
 4926                f    d(
 4927                    e,
 4928                    f
 4929                )
 4930        ˇ
 4931            )
 4932        );
 4933    "});
 4934
 4935    // Copy an indented block, starting mid-line
 4936    cx.set_state(indoc! {"
 4937        const a: B = (
 4938            c(),
 4939            somethin«g(
 4940                e,
 4941                f
 4942            )ˇ»
 4943        );
 4944    "});
 4945    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4946
 4947    // Paste it on a line with a lower indent level
 4948    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 4949    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4950    cx.assert_editor_state(indoc! {"
 4951        const a: B = (
 4952            c(),
 4953            something(
 4954                e,
 4955                f
 4956            )
 4957        );
 4958        g(
 4959            e,
 4960            f
 4961"});
 4962}
 4963
 4964#[gpui::test]
 4965async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4966    init_test(cx, |_| {});
 4967
 4968    cx.write_to_clipboard(ClipboardItem::new_string(
 4969        "    d(\n        e\n    );\n".into(),
 4970    ));
 4971
 4972    let mut cx = EditorTestContext::new(cx).await;
 4973    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4974
 4975    cx.set_state(indoc! {"
 4976        fn a() {
 4977            b();
 4978            if c() {
 4979                ˇ
 4980            }
 4981        }
 4982    "});
 4983
 4984    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4985    cx.assert_editor_state(indoc! {"
 4986        fn a() {
 4987            b();
 4988            if c() {
 4989                d(
 4990                    e
 4991                );
 4992        ˇ
 4993            }
 4994        }
 4995    "});
 4996
 4997    cx.set_state(indoc! {"
 4998        fn a() {
 4999            b();
 5000            ˇ
 5001        }
 5002    "});
 5003
 5004    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5005    cx.assert_editor_state(indoc! {"
 5006        fn a() {
 5007            b();
 5008            d(
 5009                e
 5010            );
 5011        ˇ
 5012        }
 5013    "});
 5014}
 5015
 5016#[gpui::test]
 5017fn test_select_all(cx: &mut TestAppContext) {
 5018    init_test(cx, |_| {});
 5019
 5020    let editor = cx.add_window(|window, cx| {
 5021        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5022        build_editor(buffer, window, cx)
 5023    });
 5024    _ = editor.update(cx, |editor, window, cx| {
 5025        editor.select_all(&SelectAll, window, cx);
 5026        assert_eq!(
 5027            editor.selections.display_ranges(cx),
 5028            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5029        );
 5030    });
 5031}
 5032
 5033#[gpui::test]
 5034fn test_select_line(cx: &mut TestAppContext) {
 5035    init_test(cx, |_| {});
 5036
 5037    let editor = cx.add_window(|window, cx| {
 5038        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5039        build_editor(buffer, window, cx)
 5040    });
 5041    _ = editor.update(cx, |editor, window, cx| {
 5042        editor.change_selections(None, window, cx, |s| {
 5043            s.select_display_ranges([
 5044                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5045                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5046                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5047                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5048            ])
 5049        });
 5050        editor.select_line(&SelectLine, window, cx);
 5051        assert_eq!(
 5052            editor.selections.display_ranges(cx),
 5053            vec![
 5054                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5055                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5056            ]
 5057        );
 5058    });
 5059
 5060    _ = editor.update(cx, |editor, window, cx| {
 5061        editor.select_line(&SelectLine, window, cx);
 5062        assert_eq!(
 5063            editor.selections.display_ranges(cx),
 5064            vec![
 5065                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5066                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5067            ]
 5068        );
 5069    });
 5070
 5071    _ = editor.update(cx, |editor, window, cx| {
 5072        editor.select_line(&SelectLine, window, cx);
 5073        assert_eq!(
 5074            editor.selections.display_ranges(cx),
 5075            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5076        );
 5077    });
 5078}
 5079
 5080#[gpui::test]
 5081async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5082    init_test(cx, |_| {});
 5083    let mut cx = EditorTestContext::new(cx).await;
 5084
 5085    #[track_caller]
 5086    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5087        cx.set_state(initial_state);
 5088        cx.update_editor(|e, window, cx| {
 5089            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5090        });
 5091        cx.assert_editor_state(expected_state);
 5092    }
 5093
 5094    // Selection starts and ends at the middle of lines, left-to-right
 5095    test(
 5096        &mut cx,
 5097        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5098        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5099    );
 5100    // Same thing, right-to-left
 5101    test(
 5102        &mut cx,
 5103        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5104        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5105    );
 5106
 5107    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5108    test(
 5109        &mut cx,
 5110        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5111        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5112    );
 5113    // Same thing, right-to-left
 5114    test(
 5115        &mut cx,
 5116        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5117        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5118    );
 5119
 5120    // Whole buffer, left-to-right, last line ends with newline
 5121    test(
 5122        &mut cx,
 5123        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5124        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5125    );
 5126    // Same thing, right-to-left
 5127    test(
 5128        &mut cx,
 5129        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5130        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5131    );
 5132
 5133    // Starts at the end of a line, ends at the start of another
 5134    test(
 5135        &mut cx,
 5136        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5137        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5138    );
 5139}
 5140
 5141#[gpui::test]
 5142async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5143    init_test(cx, |_| {});
 5144
 5145    let editor = cx.add_window(|window, cx| {
 5146        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5147        build_editor(buffer, window, cx)
 5148    });
 5149
 5150    // setup
 5151    _ = editor.update(cx, |editor, window, cx| {
 5152        editor.fold_creases(
 5153            vec![
 5154                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5155                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5156                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5157            ],
 5158            true,
 5159            window,
 5160            cx,
 5161        );
 5162        assert_eq!(
 5163            editor.display_text(cx),
 5164            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5165        );
 5166    });
 5167
 5168    _ = editor.update(cx, |editor, window, cx| {
 5169        editor.change_selections(None, window, cx, |s| {
 5170            s.select_display_ranges([
 5171                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5172                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5173                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5174                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5175            ])
 5176        });
 5177        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5178        assert_eq!(
 5179            editor.display_text(cx),
 5180            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5181        );
 5182    });
 5183    EditorTestContext::for_editor(editor, cx)
 5184        .await
 5185        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5186
 5187    _ = editor.update(cx, |editor, window, cx| {
 5188        editor.change_selections(None, window, cx, |s| {
 5189            s.select_display_ranges([
 5190                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5191            ])
 5192        });
 5193        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5194        assert_eq!(
 5195            editor.display_text(cx),
 5196            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5197        );
 5198        assert_eq!(
 5199            editor.selections.display_ranges(cx),
 5200            [
 5201                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5202                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5203                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5204                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5205                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5206                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5207                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5208            ]
 5209        );
 5210    });
 5211    EditorTestContext::for_editor(editor, cx)
 5212        .await
 5213        .assert_editor_state(
 5214            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5215        );
 5216}
 5217
 5218#[gpui::test]
 5219async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5220    init_test(cx, |_| {});
 5221
 5222    let mut cx = EditorTestContext::new(cx).await;
 5223
 5224    cx.set_state(indoc!(
 5225        r#"abc
 5226           defˇghi
 5227
 5228           jk
 5229           nlmo
 5230           "#
 5231    ));
 5232
 5233    cx.update_editor(|editor, window, cx| {
 5234        editor.add_selection_above(&Default::default(), window, cx);
 5235    });
 5236
 5237    cx.assert_editor_state(indoc!(
 5238        r#"abcˇ
 5239           defˇghi
 5240
 5241           jk
 5242           nlmo
 5243           "#
 5244    ));
 5245
 5246    cx.update_editor(|editor, window, cx| {
 5247        editor.add_selection_above(&Default::default(), window, cx);
 5248    });
 5249
 5250    cx.assert_editor_state(indoc!(
 5251        r#"abcˇ
 5252            defˇghi
 5253
 5254            jk
 5255            nlmo
 5256            "#
 5257    ));
 5258
 5259    cx.update_editor(|editor, window, cx| {
 5260        editor.add_selection_below(&Default::default(), window, cx);
 5261    });
 5262
 5263    cx.assert_editor_state(indoc!(
 5264        r#"abc
 5265           defˇghi
 5266
 5267           jk
 5268           nlmo
 5269           "#
 5270    ));
 5271
 5272    cx.update_editor(|editor, window, cx| {
 5273        editor.undo_selection(&Default::default(), window, cx);
 5274    });
 5275
 5276    cx.assert_editor_state(indoc!(
 5277        r#"abcˇ
 5278           defˇghi
 5279
 5280           jk
 5281           nlmo
 5282           "#
 5283    ));
 5284
 5285    cx.update_editor(|editor, window, cx| {
 5286        editor.redo_selection(&Default::default(), window, cx);
 5287    });
 5288
 5289    cx.assert_editor_state(indoc!(
 5290        r#"abc
 5291           defˇghi
 5292
 5293           jk
 5294           nlmo
 5295           "#
 5296    ));
 5297
 5298    cx.update_editor(|editor, window, cx| {
 5299        editor.add_selection_below(&Default::default(), window, cx);
 5300    });
 5301
 5302    cx.assert_editor_state(indoc!(
 5303        r#"abc
 5304           defˇghi
 5305
 5306           jk
 5307           nlmˇo
 5308           "#
 5309    ));
 5310
 5311    cx.update_editor(|editor, window, cx| {
 5312        editor.add_selection_below(&Default::default(), window, cx);
 5313    });
 5314
 5315    cx.assert_editor_state(indoc!(
 5316        r#"abc
 5317           defˇghi
 5318
 5319           jk
 5320           nlmˇo
 5321           "#
 5322    ));
 5323
 5324    // change selections
 5325    cx.set_state(indoc!(
 5326        r#"abc
 5327           def«ˇg»hi
 5328
 5329           jk
 5330           nlmo
 5331           "#
 5332    ));
 5333
 5334    cx.update_editor(|editor, window, cx| {
 5335        editor.add_selection_below(&Default::default(), window, cx);
 5336    });
 5337
 5338    cx.assert_editor_state(indoc!(
 5339        r#"abc
 5340           def«ˇg»hi
 5341
 5342           jk
 5343           nlm«ˇo»
 5344           "#
 5345    ));
 5346
 5347    cx.update_editor(|editor, window, cx| {
 5348        editor.add_selection_below(&Default::default(), window, cx);
 5349    });
 5350
 5351    cx.assert_editor_state(indoc!(
 5352        r#"abc
 5353           def«ˇg»hi
 5354
 5355           jk
 5356           nlm«ˇo»
 5357           "#
 5358    ));
 5359
 5360    cx.update_editor(|editor, window, cx| {
 5361        editor.add_selection_above(&Default::default(), window, cx);
 5362    });
 5363
 5364    cx.assert_editor_state(indoc!(
 5365        r#"abc
 5366           def«ˇg»hi
 5367
 5368           jk
 5369           nlmo
 5370           "#
 5371    ));
 5372
 5373    cx.update_editor(|editor, window, cx| {
 5374        editor.add_selection_above(&Default::default(), window, cx);
 5375    });
 5376
 5377    cx.assert_editor_state(indoc!(
 5378        r#"abc
 5379           def«ˇg»hi
 5380
 5381           jk
 5382           nlmo
 5383           "#
 5384    ));
 5385
 5386    // Change selections again
 5387    cx.set_state(indoc!(
 5388        r#"a«bc
 5389           defgˇ»hi
 5390
 5391           jk
 5392           nlmo
 5393           "#
 5394    ));
 5395
 5396    cx.update_editor(|editor, window, cx| {
 5397        editor.add_selection_below(&Default::default(), window, cx);
 5398    });
 5399
 5400    cx.assert_editor_state(indoc!(
 5401        r#"a«bcˇ»
 5402           d«efgˇ»hi
 5403
 5404           j«kˇ»
 5405           nlmo
 5406           "#
 5407    ));
 5408
 5409    cx.update_editor(|editor, window, cx| {
 5410        editor.add_selection_below(&Default::default(), window, cx);
 5411    });
 5412    cx.assert_editor_state(indoc!(
 5413        r#"a«bcˇ»
 5414           d«efgˇ»hi
 5415
 5416           j«kˇ»
 5417           n«lmoˇ»
 5418           "#
 5419    ));
 5420    cx.update_editor(|editor, window, cx| {
 5421        editor.add_selection_above(&Default::default(), window, cx);
 5422    });
 5423
 5424    cx.assert_editor_state(indoc!(
 5425        r#"a«bcˇ»
 5426           d«efgˇ»hi
 5427
 5428           j«kˇ»
 5429           nlmo
 5430           "#
 5431    ));
 5432
 5433    // Change selections again
 5434    cx.set_state(indoc!(
 5435        r#"abc
 5436           d«ˇefghi
 5437
 5438           jk
 5439           nlm»o
 5440           "#
 5441    ));
 5442
 5443    cx.update_editor(|editor, window, cx| {
 5444        editor.add_selection_above(&Default::default(), window, cx);
 5445    });
 5446
 5447    cx.assert_editor_state(indoc!(
 5448        r#"a«ˇbc»
 5449           d«ˇef»ghi
 5450
 5451           j«ˇk»
 5452           n«ˇlm»o
 5453           "#
 5454    ));
 5455
 5456    cx.update_editor(|editor, window, cx| {
 5457        editor.add_selection_below(&Default::default(), window, cx);
 5458    });
 5459
 5460    cx.assert_editor_state(indoc!(
 5461        r#"abc
 5462           d«ˇef»ghi
 5463
 5464           j«ˇk»
 5465           n«ˇlm»o
 5466           "#
 5467    ));
 5468}
 5469
 5470#[gpui::test]
 5471async fn test_select_next(cx: &mut TestAppContext) {
 5472    init_test(cx, |_| {});
 5473
 5474    let mut cx = EditorTestContext::new(cx).await;
 5475    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5476
 5477    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5478        .unwrap();
 5479    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5480
 5481    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5482        .unwrap();
 5483    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5484
 5485    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5486    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5487
 5488    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5489    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5490
 5491    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5492        .unwrap();
 5493    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5494
 5495    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5496        .unwrap();
 5497    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5498}
 5499
 5500#[gpui::test]
 5501async fn test_select_all_matches(cx: &mut TestAppContext) {
 5502    init_test(cx, |_| {});
 5503
 5504    let mut cx = EditorTestContext::new(cx).await;
 5505
 5506    // Test caret-only selections
 5507    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5508    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5509        .unwrap();
 5510    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5511
 5512    // Test left-to-right selections
 5513    cx.set_state("abc\n«abcˇ»\nabc");
 5514    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5515        .unwrap();
 5516    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5517
 5518    // Test right-to-left selections
 5519    cx.set_state("abc\n«ˇabc»\nabc");
 5520    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5521        .unwrap();
 5522    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5523
 5524    // Test selecting whitespace with caret selection
 5525    cx.set_state("abc\nˇ   abc\nabc");
 5526    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5527        .unwrap();
 5528    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5529
 5530    // Test selecting whitespace with left-to-right selection
 5531    cx.set_state("abc\n«ˇ  »abc\nabc");
 5532    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5533        .unwrap();
 5534    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5535
 5536    // Test no matches with right-to-left selection
 5537    cx.set_state("abc\n«  ˇ»abc\nabc");
 5538    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5539        .unwrap();
 5540    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5541}
 5542
 5543#[gpui::test]
 5544async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5545    init_test(cx, |_| {});
 5546
 5547    let mut cx = EditorTestContext::new(cx).await;
 5548    cx.set_state(
 5549        r#"let foo = 2;
 5550lˇet foo = 2;
 5551let fooˇ = 2;
 5552let foo = 2;
 5553let foo = ˇ2;"#,
 5554    );
 5555
 5556    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5557        .unwrap();
 5558    cx.assert_editor_state(
 5559        r#"let foo = 2;
 5560«letˇ» foo = 2;
 5561let «fooˇ» = 2;
 5562let foo = 2;
 5563let foo = «2ˇ»;"#,
 5564    );
 5565
 5566    // noop for multiple selections with different contents
 5567    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5568        .unwrap();
 5569    cx.assert_editor_state(
 5570        r#"let foo = 2;
 5571«letˇ» foo = 2;
 5572let «fooˇ» = 2;
 5573let foo = 2;
 5574let foo = «2ˇ»;"#,
 5575    );
 5576}
 5577
 5578#[gpui::test]
 5579async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5580    init_test(cx, |_| {});
 5581
 5582    let mut cx =
 5583        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5584
 5585    cx.assert_editor_state(indoc! {"
 5586        ˇbbb
 5587        ccc
 5588
 5589        bbb
 5590        ccc
 5591        "});
 5592    cx.dispatch_action(SelectPrevious::default());
 5593    cx.assert_editor_state(indoc! {"
 5594                «bbbˇ»
 5595                ccc
 5596
 5597                bbb
 5598                ccc
 5599                "});
 5600    cx.dispatch_action(SelectPrevious::default());
 5601    cx.assert_editor_state(indoc! {"
 5602                «bbbˇ»
 5603                ccc
 5604
 5605                «bbbˇ»
 5606                ccc
 5607                "});
 5608}
 5609
 5610#[gpui::test]
 5611async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5612    init_test(cx, |_| {});
 5613
 5614    let mut cx = EditorTestContext::new(cx).await;
 5615    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5616
 5617    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5618        .unwrap();
 5619    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5620
 5621    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5622        .unwrap();
 5623    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5624
 5625    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5626    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5627
 5628    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5629    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5630
 5631    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5632        .unwrap();
 5633    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5634
 5635    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5636        .unwrap();
 5637    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5638
 5639    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5640        .unwrap();
 5641    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5642}
 5643
 5644#[gpui::test]
 5645async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5646    init_test(cx, |_| {});
 5647
 5648    let mut cx = EditorTestContext::new(cx).await;
 5649    cx.set_state("");
 5650
 5651    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5652        .unwrap();
 5653    cx.assert_editor_state("«aˇ»");
 5654    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5655        .unwrap();
 5656    cx.assert_editor_state("«aˇ»");
 5657}
 5658
 5659#[gpui::test]
 5660async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5661    init_test(cx, |_| {});
 5662
 5663    let mut cx = EditorTestContext::new(cx).await;
 5664    cx.set_state(
 5665        r#"let foo = 2;
 5666lˇet foo = 2;
 5667let fooˇ = 2;
 5668let foo = 2;
 5669let foo = ˇ2;"#,
 5670    );
 5671
 5672    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5673        .unwrap();
 5674    cx.assert_editor_state(
 5675        r#"let foo = 2;
 5676«letˇ» foo = 2;
 5677let «fooˇ» = 2;
 5678let foo = 2;
 5679let foo = «2ˇ»;"#,
 5680    );
 5681
 5682    // noop for multiple selections with different contents
 5683    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5684        .unwrap();
 5685    cx.assert_editor_state(
 5686        r#"let foo = 2;
 5687«letˇ» foo = 2;
 5688let «fooˇ» = 2;
 5689let foo = 2;
 5690let foo = «2ˇ»;"#,
 5691    );
 5692}
 5693
 5694#[gpui::test]
 5695async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5696    init_test(cx, |_| {});
 5697
 5698    let mut cx = EditorTestContext::new(cx).await;
 5699    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5700
 5701    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5702        .unwrap();
 5703    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5704
 5705    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5706        .unwrap();
 5707    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5708
 5709    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5710    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5711
 5712    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5713    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5714
 5715    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5716        .unwrap();
 5717    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5718
 5719    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5720        .unwrap();
 5721    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5722}
 5723
 5724#[gpui::test]
 5725async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5726    init_test(cx, |_| {});
 5727
 5728    let language = Arc::new(Language::new(
 5729        LanguageConfig::default(),
 5730        Some(tree_sitter_rust::LANGUAGE.into()),
 5731    ));
 5732
 5733    let text = r#"
 5734        use mod1::mod2::{mod3, mod4};
 5735
 5736        fn fn_1(param1: bool, param2: &str) {
 5737            let var1 = "text";
 5738        }
 5739    "#
 5740    .unindent();
 5741
 5742    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5743    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5744    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5745
 5746    editor
 5747        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5748        .await;
 5749
 5750    editor.update_in(cx, |editor, window, cx| {
 5751        editor.change_selections(None, window, cx, |s| {
 5752            s.select_display_ranges([
 5753                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5754                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5755                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5756            ]);
 5757        });
 5758        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5759    });
 5760    editor.update(cx, |editor, cx| {
 5761        assert_text_with_selections(
 5762            editor,
 5763            indoc! {r#"
 5764                use mod1::mod2::{mod3, «mod4ˇ»};
 5765
 5766                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5767                    let var1 = "«textˇ»";
 5768                }
 5769            "#},
 5770            cx,
 5771        );
 5772    });
 5773
 5774    editor.update_in(cx, |editor, window, cx| {
 5775        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5776    });
 5777    editor.update(cx, |editor, cx| {
 5778        assert_text_with_selections(
 5779            editor,
 5780            indoc! {r#"
 5781                use mod1::mod2::«{mod3, mod4}ˇ»;
 5782
 5783                «ˇfn fn_1(param1: bool, param2: &str) {
 5784                    let var1 = "text";
 5785 5786            "#},
 5787            cx,
 5788        );
 5789    });
 5790
 5791    editor.update_in(cx, |editor, window, cx| {
 5792        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5793    });
 5794    assert_eq!(
 5795        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5796        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5797    );
 5798
 5799    // Trying to expand the selected syntax node one more time has no effect.
 5800    editor.update_in(cx, |editor, window, cx| {
 5801        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5802    });
 5803    assert_eq!(
 5804        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5805        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5806    );
 5807
 5808    editor.update_in(cx, |editor, window, cx| {
 5809        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5810    });
 5811    editor.update(cx, |editor, cx| {
 5812        assert_text_with_selections(
 5813            editor,
 5814            indoc! {r#"
 5815                use mod1::mod2::«{mod3, mod4}ˇ»;
 5816
 5817                «ˇfn fn_1(param1: bool, param2: &str) {
 5818                    let var1 = "text";
 5819 5820            "#},
 5821            cx,
 5822        );
 5823    });
 5824
 5825    editor.update_in(cx, |editor, window, cx| {
 5826        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5827    });
 5828    editor.update(cx, |editor, cx| {
 5829        assert_text_with_selections(
 5830            editor,
 5831            indoc! {r#"
 5832                use mod1::mod2::{mod3, «mod4ˇ»};
 5833
 5834                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5835                    let var1 = "«textˇ»";
 5836                }
 5837            "#},
 5838            cx,
 5839        );
 5840    });
 5841
 5842    editor.update_in(cx, |editor, window, cx| {
 5843        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5844    });
 5845    editor.update(cx, |editor, cx| {
 5846        assert_text_with_selections(
 5847            editor,
 5848            indoc! {r#"
 5849                use mod1::mod2::{mod3, mo«ˇ»d4};
 5850
 5851                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5852                    let var1 = "te«ˇ»xt";
 5853                }
 5854            "#},
 5855            cx,
 5856        );
 5857    });
 5858
 5859    // Trying to shrink the selected syntax node one more time has no effect.
 5860    editor.update_in(cx, |editor, window, cx| {
 5861        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5862    });
 5863    editor.update_in(cx, |editor, _, cx| {
 5864        assert_text_with_selections(
 5865            editor,
 5866            indoc! {r#"
 5867                use mod1::mod2::{mod3, mo«ˇ»d4};
 5868
 5869                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5870                    let var1 = "te«ˇ»xt";
 5871                }
 5872            "#},
 5873            cx,
 5874        );
 5875    });
 5876
 5877    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5878    // a fold.
 5879    editor.update_in(cx, |editor, window, cx| {
 5880        editor.fold_creases(
 5881            vec![
 5882                Crease::simple(
 5883                    Point::new(0, 21)..Point::new(0, 24),
 5884                    FoldPlaceholder::test(),
 5885                ),
 5886                Crease::simple(
 5887                    Point::new(3, 20)..Point::new(3, 22),
 5888                    FoldPlaceholder::test(),
 5889                ),
 5890            ],
 5891            true,
 5892            window,
 5893            cx,
 5894        );
 5895        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5896    });
 5897    editor.update(cx, |editor, cx| {
 5898        assert_text_with_selections(
 5899            editor,
 5900            indoc! {r#"
 5901                use mod1::mod2::«{mod3, mod4}ˇ»;
 5902
 5903                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5904                    «let var1 = "text";ˇ»
 5905                }
 5906            "#},
 5907            cx,
 5908        );
 5909    });
 5910}
 5911
 5912#[gpui::test]
 5913async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5914    init_test(cx, |_| {});
 5915
 5916    let base_text = r#"
 5917        impl A {
 5918            // this is an uncommitted comment
 5919
 5920            fn b() {
 5921                c();
 5922            }
 5923
 5924            // this is another uncommitted comment
 5925
 5926            fn d() {
 5927                // e
 5928                // f
 5929            }
 5930        }
 5931
 5932        fn g() {
 5933            // h
 5934        }
 5935    "#
 5936    .unindent();
 5937
 5938    let text = r#"
 5939        ˇimpl A {
 5940
 5941            fn b() {
 5942                c();
 5943            }
 5944
 5945            fn d() {
 5946                // e
 5947                // f
 5948            }
 5949        }
 5950
 5951        fn g() {
 5952            // h
 5953        }
 5954    "#
 5955    .unindent();
 5956
 5957    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5958    cx.set_state(&text);
 5959    cx.set_head_text(&base_text);
 5960    cx.update_editor(|editor, window, cx| {
 5961        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5962    });
 5963
 5964    cx.assert_state_with_diff(
 5965        "
 5966        ˇimpl A {
 5967      -     // this is an uncommitted comment
 5968
 5969            fn b() {
 5970                c();
 5971            }
 5972
 5973      -     // this is another uncommitted comment
 5974      -
 5975            fn d() {
 5976                // e
 5977                // f
 5978            }
 5979        }
 5980
 5981        fn g() {
 5982            // h
 5983        }
 5984    "
 5985        .unindent(),
 5986    );
 5987
 5988    let expected_display_text = "
 5989        impl A {
 5990            // this is an uncommitted comment
 5991
 5992            fn b() {
 5993 5994            }
 5995
 5996            // this is another uncommitted comment
 5997
 5998            fn d() {
 5999 6000            }
 6001        }
 6002
 6003        fn g() {
 6004 6005        }
 6006        "
 6007    .unindent();
 6008
 6009    cx.update_editor(|editor, window, cx| {
 6010        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6011        assert_eq!(editor.display_text(cx), expected_display_text);
 6012    });
 6013}
 6014
 6015#[gpui::test]
 6016async fn test_autoindent(cx: &mut TestAppContext) {
 6017    init_test(cx, |_| {});
 6018
 6019    let language = Arc::new(
 6020        Language::new(
 6021            LanguageConfig {
 6022                brackets: BracketPairConfig {
 6023                    pairs: vec![
 6024                        BracketPair {
 6025                            start: "{".to_string(),
 6026                            end: "}".to_string(),
 6027                            close: false,
 6028                            surround: false,
 6029                            newline: true,
 6030                        },
 6031                        BracketPair {
 6032                            start: "(".to_string(),
 6033                            end: ")".to_string(),
 6034                            close: false,
 6035                            surround: false,
 6036                            newline: true,
 6037                        },
 6038                    ],
 6039                    ..Default::default()
 6040                },
 6041                ..Default::default()
 6042            },
 6043            Some(tree_sitter_rust::LANGUAGE.into()),
 6044        )
 6045        .with_indents_query(
 6046            r#"
 6047                (_ "(" ")" @end) @indent
 6048                (_ "{" "}" @end) @indent
 6049            "#,
 6050        )
 6051        .unwrap(),
 6052    );
 6053
 6054    let text = "fn a() {}";
 6055
 6056    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6057    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6058    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6059    editor
 6060        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6061        .await;
 6062
 6063    editor.update_in(cx, |editor, window, cx| {
 6064        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6065        editor.newline(&Newline, window, cx);
 6066        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6067        assert_eq!(
 6068            editor.selections.ranges(cx),
 6069            &[
 6070                Point::new(1, 4)..Point::new(1, 4),
 6071                Point::new(3, 4)..Point::new(3, 4),
 6072                Point::new(5, 0)..Point::new(5, 0)
 6073            ]
 6074        );
 6075    });
 6076}
 6077
 6078#[gpui::test]
 6079async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6080    init_test(cx, |_| {});
 6081
 6082    {
 6083        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6084        cx.set_state(indoc! {"
 6085            impl A {
 6086
 6087                fn b() {}
 6088
 6089            «fn c() {
 6090
 6091            }ˇ»
 6092            }
 6093        "});
 6094
 6095        cx.update_editor(|editor, window, cx| {
 6096            editor.autoindent(&Default::default(), window, cx);
 6097        });
 6098
 6099        cx.assert_editor_state(indoc! {"
 6100            impl A {
 6101
 6102                fn b() {}
 6103
 6104                «fn c() {
 6105
 6106                }ˇ»
 6107            }
 6108        "});
 6109    }
 6110
 6111    {
 6112        let mut cx = EditorTestContext::new_multibuffer(
 6113            cx,
 6114            [indoc! { "
 6115                impl A {
 6116                «
 6117                // a
 6118                fn b(){}
 6119                »
 6120                «
 6121                    }
 6122                    fn c(){}
 6123                »
 6124            "}],
 6125        );
 6126
 6127        let buffer = cx.update_editor(|editor, _, cx| {
 6128            let buffer = editor.buffer().update(cx, |buffer, _| {
 6129                buffer.all_buffers().iter().next().unwrap().clone()
 6130            });
 6131            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6132            buffer
 6133        });
 6134
 6135        cx.run_until_parked();
 6136        cx.update_editor(|editor, window, cx| {
 6137            editor.select_all(&Default::default(), window, cx);
 6138            editor.autoindent(&Default::default(), window, cx)
 6139        });
 6140        cx.run_until_parked();
 6141
 6142        cx.update(|_, cx| {
 6143            pretty_assertions::assert_eq!(
 6144                buffer.read(cx).text(),
 6145                indoc! { "
 6146                    impl A {
 6147
 6148                        // a
 6149                        fn b(){}
 6150
 6151
 6152                    }
 6153                    fn c(){}
 6154
 6155                " }
 6156            )
 6157        });
 6158    }
 6159}
 6160
 6161#[gpui::test]
 6162async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6163    init_test(cx, |_| {});
 6164
 6165    let mut cx = EditorTestContext::new(cx).await;
 6166
 6167    let language = Arc::new(Language::new(
 6168        LanguageConfig {
 6169            brackets: BracketPairConfig {
 6170                pairs: vec![
 6171                    BracketPair {
 6172                        start: "{".to_string(),
 6173                        end: "}".to_string(),
 6174                        close: true,
 6175                        surround: true,
 6176                        newline: true,
 6177                    },
 6178                    BracketPair {
 6179                        start: "(".to_string(),
 6180                        end: ")".to_string(),
 6181                        close: true,
 6182                        surround: true,
 6183                        newline: true,
 6184                    },
 6185                    BracketPair {
 6186                        start: "/*".to_string(),
 6187                        end: " */".to_string(),
 6188                        close: true,
 6189                        surround: true,
 6190                        newline: true,
 6191                    },
 6192                    BracketPair {
 6193                        start: "[".to_string(),
 6194                        end: "]".to_string(),
 6195                        close: false,
 6196                        surround: false,
 6197                        newline: true,
 6198                    },
 6199                    BracketPair {
 6200                        start: "\"".to_string(),
 6201                        end: "\"".to_string(),
 6202                        close: true,
 6203                        surround: true,
 6204                        newline: false,
 6205                    },
 6206                    BracketPair {
 6207                        start: "<".to_string(),
 6208                        end: ">".to_string(),
 6209                        close: false,
 6210                        surround: true,
 6211                        newline: true,
 6212                    },
 6213                ],
 6214                ..Default::default()
 6215            },
 6216            autoclose_before: "})]".to_string(),
 6217            ..Default::default()
 6218        },
 6219        Some(tree_sitter_rust::LANGUAGE.into()),
 6220    ));
 6221
 6222    cx.language_registry().add(language.clone());
 6223    cx.update_buffer(|buffer, cx| {
 6224        buffer.set_language(Some(language), cx);
 6225    });
 6226
 6227    cx.set_state(
 6228        &r#"
 6229            🏀ˇ
 6230            εˇ
 6231            ❤️ˇ
 6232        "#
 6233        .unindent(),
 6234    );
 6235
 6236    // autoclose multiple nested brackets at multiple cursors
 6237    cx.update_editor(|editor, window, cx| {
 6238        editor.handle_input("{", window, cx);
 6239        editor.handle_input("{", window, cx);
 6240        editor.handle_input("{", window, cx);
 6241    });
 6242    cx.assert_editor_state(
 6243        &"
 6244            🏀{{{ˇ}}}
 6245            ε{{{ˇ}}}
 6246            ❤️{{{ˇ}}}
 6247        "
 6248        .unindent(),
 6249    );
 6250
 6251    // insert a different closing bracket
 6252    cx.update_editor(|editor, window, cx| {
 6253        editor.handle_input(")", window, cx);
 6254    });
 6255    cx.assert_editor_state(
 6256        &"
 6257            🏀{{{)ˇ}}}
 6258            ε{{{)ˇ}}}
 6259            ❤️{{{)ˇ}}}
 6260        "
 6261        .unindent(),
 6262    );
 6263
 6264    // skip over the auto-closed brackets when typing a closing bracket
 6265    cx.update_editor(|editor, window, cx| {
 6266        editor.move_right(&MoveRight, window, cx);
 6267        editor.handle_input("}", window, cx);
 6268        editor.handle_input("}", window, cx);
 6269        editor.handle_input("}", window, cx);
 6270    });
 6271    cx.assert_editor_state(
 6272        &"
 6273            🏀{{{)}}}}ˇ
 6274            ε{{{)}}}}ˇ
 6275            ❤️{{{)}}}}ˇ
 6276        "
 6277        .unindent(),
 6278    );
 6279
 6280    // autoclose multi-character pairs
 6281    cx.set_state(
 6282        &"
 6283            ˇ
 6284            ˇ
 6285        "
 6286        .unindent(),
 6287    );
 6288    cx.update_editor(|editor, window, cx| {
 6289        editor.handle_input("/", window, cx);
 6290        editor.handle_input("*", window, cx);
 6291    });
 6292    cx.assert_editor_state(
 6293        &"
 6294            /*ˇ */
 6295            /*ˇ */
 6296        "
 6297        .unindent(),
 6298    );
 6299
 6300    // one cursor autocloses a multi-character pair, one cursor
 6301    // does not autoclose.
 6302    cx.set_state(
 6303        &"
 6304 6305            ˇ
 6306        "
 6307        .unindent(),
 6308    );
 6309    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6310    cx.assert_editor_state(
 6311        &"
 6312            /*ˇ */
 6313 6314        "
 6315        .unindent(),
 6316    );
 6317
 6318    // Don't autoclose if the next character isn't whitespace and isn't
 6319    // listed in the language's "autoclose_before" section.
 6320    cx.set_state("ˇa b");
 6321    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6322    cx.assert_editor_state("{ˇa b");
 6323
 6324    // Don't autoclose if `close` is false for the bracket pair
 6325    cx.set_state("ˇ");
 6326    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6327    cx.assert_editor_state("");
 6328
 6329    // Surround with brackets if text is selected
 6330    cx.set_state("«aˇ» b");
 6331    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6332    cx.assert_editor_state("{«aˇ»} b");
 6333
 6334    // Autclose pair where the start and end characters are the same
 6335    cx.set_state("");
 6336    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6337    cx.assert_editor_state("a\"ˇ\"");
 6338    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6339    cx.assert_editor_state("a\"\"ˇ");
 6340
 6341    // Don't autoclose pair if autoclose is disabled
 6342    cx.set_state("ˇ");
 6343    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6344    cx.assert_editor_state("");
 6345
 6346    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6347    cx.set_state("«aˇ» b");
 6348    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6349    cx.assert_editor_state("<«aˇ»> b");
 6350}
 6351
 6352#[gpui::test]
 6353async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6354    init_test(cx, |settings| {
 6355        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6356    });
 6357
 6358    let mut cx = EditorTestContext::new(cx).await;
 6359
 6360    let language = Arc::new(Language::new(
 6361        LanguageConfig {
 6362            brackets: BracketPairConfig {
 6363                pairs: vec![
 6364                    BracketPair {
 6365                        start: "{".to_string(),
 6366                        end: "}".to_string(),
 6367                        close: true,
 6368                        surround: true,
 6369                        newline: true,
 6370                    },
 6371                    BracketPair {
 6372                        start: "(".to_string(),
 6373                        end: ")".to_string(),
 6374                        close: true,
 6375                        surround: true,
 6376                        newline: true,
 6377                    },
 6378                    BracketPair {
 6379                        start: "[".to_string(),
 6380                        end: "]".to_string(),
 6381                        close: false,
 6382                        surround: false,
 6383                        newline: true,
 6384                    },
 6385                ],
 6386                ..Default::default()
 6387            },
 6388            autoclose_before: "})]".to_string(),
 6389            ..Default::default()
 6390        },
 6391        Some(tree_sitter_rust::LANGUAGE.into()),
 6392    ));
 6393
 6394    cx.language_registry().add(language.clone());
 6395    cx.update_buffer(|buffer, cx| {
 6396        buffer.set_language(Some(language), cx);
 6397    });
 6398
 6399    cx.set_state(
 6400        &"
 6401            ˇ
 6402            ˇ
 6403            ˇ
 6404        "
 6405        .unindent(),
 6406    );
 6407
 6408    // ensure only matching closing brackets are skipped over
 6409    cx.update_editor(|editor, window, cx| {
 6410        editor.handle_input("}", window, cx);
 6411        editor.move_left(&MoveLeft, window, cx);
 6412        editor.handle_input(")", window, cx);
 6413        editor.move_left(&MoveLeft, window, cx);
 6414    });
 6415    cx.assert_editor_state(
 6416        &"
 6417            ˇ)}
 6418            ˇ)}
 6419            ˇ)}
 6420        "
 6421        .unindent(),
 6422    );
 6423
 6424    // skip-over closing brackets at multiple cursors
 6425    cx.update_editor(|editor, window, cx| {
 6426        editor.handle_input(")", window, cx);
 6427        editor.handle_input("}", window, cx);
 6428    });
 6429    cx.assert_editor_state(
 6430        &"
 6431            )}ˇ
 6432            )}ˇ
 6433            )}ˇ
 6434        "
 6435        .unindent(),
 6436    );
 6437
 6438    // ignore non-close brackets
 6439    cx.update_editor(|editor, window, cx| {
 6440        editor.handle_input("]", window, cx);
 6441        editor.move_left(&MoveLeft, window, cx);
 6442        editor.handle_input("]", window, cx);
 6443    });
 6444    cx.assert_editor_state(
 6445        &"
 6446            )}]ˇ]
 6447            )}]ˇ]
 6448            )}]ˇ]
 6449        "
 6450        .unindent(),
 6451    );
 6452}
 6453
 6454#[gpui::test]
 6455async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6456    init_test(cx, |_| {});
 6457
 6458    let mut cx = EditorTestContext::new(cx).await;
 6459
 6460    let html_language = Arc::new(
 6461        Language::new(
 6462            LanguageConfig {
 6463                name: "HTML".into(),
 6464                brackets: BracketPairConfig {
 6465                    pairs: vec![
 6466                        BracketPair {
 6467                            start: "<".into(),
 6468                            end: ">".into(),
 6469                            close: true,
 6470                            ..Default::default()
 6471                        },
 6472                        BracketPair {
 6473                            start: "{".into(),
 6474                            end: "}".into(),
 6475                            close: true,
 6476                            ..Default::default()
 6477                        },
 6478                        BracketPair {
 6479                            start: "(".into(),
 6480                            end: ")".into(),
 6481                            close: true,
 6482                            ..Default::default()
 6483                        },
 6484                    ],
 6485                    ..Default::default()
 6486                },
 6487                autoclose_before: "})]>".into(),
 6488                ..Default::default()
 6489            },
 6490            Some(tree_sitter_html::LANGUAGE.into()),
 6491        )
 6492        .with_injection_query(
 6493            r#"
 6494            (script_element
 6495                (raw_text) @injection.content
 6496                (#set! injection.language "javascript"))
 6497            "#,
 6498        )
 6499        .unwrap(),
 6500    );
 6501
 6502    let javascript_language = Arc::new(Language::new(
 6503        LanguageConfig {
 6504            name: "JavaScript".into(),
 6505            brackets: BracketPairConfig {
 6506                pairs: vec![
 6507                    BracketPair {
 6508                        start: "/*".into(),
 6509                        end: " */".into(),
 6510                        close: true,
 6511                        ..Default::default()
 6512                    },
 6513                    BracketPair {
 6514                        start: "{".into(),
 6515                        end: "}".into(),
 6516                        close: true,
 6517                        ..Default::default()
 6518                    },
 6519                    BracketPair {
 6520                        start: "(".into(),
 6521                        end: ")".into(),
 6522                        close: true,
 6523                        ..Default::default()
 6524                    },
 6525                ],
 6526                ..Default::default()
 6527            },
 6528            autoclose_before: "})]>".into(),
 6529            ..Default::default()
 6530        },
 6531        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6532    ));
 6533
 6534    cx.language_registry().add(html_language.clone());
 6535    cx.language_registry().add(javascript_language.clone());
 6536
 6537    cx.update_buffer(|buffer, cx| {
 6538        buffer.set_language(Some(html_language), cx);
 6539    });
 6540
 6541    cx.set_state(
 6542        &r#"
 6543            <body>ˇ
 6544                <script>
 6545                    var x = 1;ˇ
 6546                </script>
 6547            </body>ˇ
 6548        "#
 6549        .unindent(),
 6550    );
 6551
 6552    // Precondition: different languages are active at different locations.
 6553    cx.update_editor(|editor, window, cx| {
 6554        let snapshot = editor.snapshot(window, cx);
 6555        let cursors = editor.selections.ranges::<usize>(cx);
 6556        let languages = cursors
 6557            .iter()
 6558            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6559            .collect::<Vec<_>>();
 6560        assert_eq!(
 6561            languages,
 6562            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6563        );
 6564    });
 6565
 6566    // Angle brackets autoclose in HTML, but not JavaScript.
 6567    cx.update_editor(|editor, window, cx| {
 6568        editor.handle_input("<", window, cx);
 6569        editor.handle_input("a", window, cx);
 6570    });
 6571    cx.assert_editor_state(
 6572        &r#"
 6573            <body><aˇ>
 6574                <script>
 6575                    var x = 1;<aˇ
 6576                </script>
 6577            </body><aˇ>
 6578        "#
 6579        .unindent(),
 6580    );
 6581
 6582    // Curly braces and parens autoclose in both HTML and JavaScript.
 6583    cx.update_editor(|editor, window, cx| {
 6584        editor.handle_input(" b=", window, cx);
 6585        editor.handle_input("{", window, cx);
 6586        editor.handle_input("c", window, cx);
 6587        editor.handle_input("(", window, cx);
 6588    });
 6589    cx.assert_editor_state(
 6590        &r#"
 6591            <body><a b={c(ˇ)}>
 6592                <script>
 6593                    var x = 1;<a b={c(ˇ)}
 6594                </script>
 6595            </body><a b={c(ˇ)}>
 6596        "#
 6597        .unindent(),
 6598    );
 6599
 6600    // Brackets that were already autoclosed are skipped.
 6601    cx.update_editor(|editor, window, cx| {
 6602        editor.handle_input(")", window, cx);
 6603        editor.handle_input("d", window, cx);
 6604        editor.handle_input("}", window, cx);
 6605    });
 6606    cx.assert_editor_state(
 6607        &r#"
 6608            <body><a b={c()d}ˇ>
 6609                <script>
 6610                    var x = 1;<a b={c()d}ˇ
 6611                </script>
 6612            </body><a b={c()d}ˇ>
 6613        "#
 6614        .unindent(),
 6615    );
 6616    cx.update_editor(|editor, window, cx| {
 6617        editor.handle_input(">", window, cx);
 6618    });
 6619    cx.assert_editor_state(
 6620        &r#"
 6621            <body><a b={c()d}>ˇ
 6622                <script>
 6623                    var x = 1;<a b={c()d}>ˇ
 6624                </script>
 6625            </body><a b={c()d}>ˇ
 6626        "#
 6627        .unindent(),
 6628    );
 6629
 6630    // Reset
 6631    cx.set_state(
 6632        &r#"
 6633            <body>ˇ
 6634                <script>
 6635                    var x = 1;ˇ
 6636                </script>
 6637            </body>ˇ
 6638        "#
 6639        .unindent(),
 6640    );
 6641
 6642    cx.update_editor(|editor, window, cx| {
 6643        editor.handle_input("<", window, cx);
 6644    });
 6645    cx.assert_editor_state(
 6646        &r#"
 6647            <body><ˇ>
 6648                <script>
 6649                    var x = 1;<ˇ
 6650                </script>
 6651            </body><ˇ>
 6652        "#
 6653        .unindent(),
 6654    );
 6655
 6656    // When backspacing, the closing angle brackets are removed.
 6657    cx.update_editor(|editor, window, cx| {
 6658        editor.backspace(&Backspace, window, cx);
 6659    });
 6660    cx.assert_editor_state(
 6661        &r#"
 6662            <body>ˇ
 6663                <script>
 6664                    var x = 1;ˇ
 6665                </script>
 6666            </body>ˇ
 6667        "#
 6668        .unindent(),
 6669    );
 6670
 6671    // Block comments autoclose in JavaScript, but not HTML.
 6672    cx.update_editor(|editor, window, cx| {
 6673        editor.handle_input("/", window, cx);
 6674        editor.handle_input("*", window, cx);
 6675    });
 6676    cx.assert_editor_state(
 6677        &r#"
 6678            <body>/*ˇ
 6679                <script>
 6680                    var x = 1;/*ˇ */
 6681                </script>
 6682            </body>/*ˇ
 6683        "#
 6684        .unindent(),
 6685    );
 6686}
 6687
 6688#[gpui::test]
 6689async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6690    init_test(cx, |_| {});
 6691
 6692    let mut cx = EditorTestContext::new(cx).await;
 6693
 6694    let rust_language = Arc::new(
 6695        Language::new(
 6696            LanguageConfig {
 6697                name: "Rust".into(),
 6698                brackets: serde_json::from_value(json!([
 6699                    { "start": "{", "end": "}", "close": true, "newline": true },
 6700                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6701                ]))
 6702                .unwrap(),
 6703                autoclose_before: "})]>".into(),
 6704                ..Default::default()
 6705            },
 6706            Some(tree_sitter_rust::LANGUAGE.into()),
 6707        )
 6708        .with_override_query("(string_literal) @string")
 6709        .unwrap(),
 6710    );
 6711
 6712    cx.language_registry().add(rust_language.clone());
 6713    cx.update_buffer(|buffer, cx| {
 6714        buffer.set_language(Some(rust_language), cx);
 6715    });
 6716
 6717    cx.set_state(
 6718        &r#"
 6719            let x = ˇ
 6720        "#
 6721        .unindent(),
 6722    );
 6723
 6724    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6725    cx.update_editor(|editor, window, cx| {
 6726        editor.handle_input("\"", window, cx);
 6727    });
 6728    cx.assert_editor_state(
 6729        &r#"
 6730            let x = "ˇ"
 6731        "#
 6732        .unindent(),
 6733    );
 6734
 6735    // Inserting another quotation mark. The cursor moves across the existing
 6736    // automatically-inserted quotation mark.
 6737    cx.update_editor(|editor, window, cx| {
 6738        editor.handle_input("\"", window, cx);
 6739    });
 6740    cx.assert_editor_state(
 6741        &r#"
 6742            let x = ""ˇ
 6743        "#
 6744        .unindent(),
 6745    );
 6746
 6747    // Reset
 6748    cx.set_state(
 6749        &r#"
 6750            let x = ˇ
 6751        "#
 6752        .unindent(),
 6753    );
 6754
 6755    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6756    cx.update_editor(|editor, window, cx| {
 6757        editor.handle_input("\"", window, cx);
 6758        editor.handle_input(" ", window, cx);
 6759        editor.move_left(&Default::default(), window, cx);
 6760        editor.handle_input("\\", window, cx);
 6761        editor.handle_input("\"", window, cx);
 6762    });
 6763    cx.assert_editor_state(
 6764        &r#"
 6765            let x = "\"ˇ "
 6766        "#
 6767        .unindent(),
 6768    );
 6769
 6770    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6771    // mark. Nothing is inserted.
 6772    cx.update_editor(|editor, window, cx| {
 6773        editor.move_right(&Default::default(), window, cx);
 6774        editor.handle_input("\"", window, cx);
 6775    });
 6776    cx.assert_editor_state(
 6777        &r#"
 6778            let x = "\" "ˇ
 6779        "#
 6780        .unindent(),
 6781    );
 6782}
 6783
 6784#[gpui::test]
 6785async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6786    init_test(cx, |_| {});
 6787
 6788    let language = Arc::new(Language::new(
 6789        LanguageConfig {
 6790            brackets: BracketPairConfig {
 6791                pairs: vec![
 6792                    BracketPair {
 6793                        start: "{".to_string(),
 6794                        end: "}".to_string(),
 6795                        close: true,
 6796                        surround: true,
 6797                        newline: true,
 6798                    },
 6799                    BracketPair {
 6800                        start: "/* ".to_string(),
 6801                        end: "*/".to_string(),
 6802                        close: true,
 6803                        surround: true,
 6804                        ..Default::default()
 6805                    },
 6806                ],
 6807                ..Default::default()
 6808            },
 6809            ..Default::default()
 6810        },
 6811        Some(tree_sitter_rust::LANGUAGE.into()),
 6812    ));
 6813
 6814    let text = r#"
 6815        a
 6816        b
 6817        c
 6818    "#
 6819    .unindent();
 6820
 6821    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6822    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6823    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6824    editor
 6825        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6826        .await;
 6827
 6828    editor.update_in(cx, |editor, window, cx| {
 6829        editor.change_selections(None, window, cx, |s| {
 6830            s.select_display_ranges([
 6831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6832                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6833                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6834            ])
 6835        });
 6836
 6837        editor.handle_input("{", window, cx);
 6838        editor.handle_input("{", window, cx);
 6839        editor.handle_input("{", window, cx);
 6840        assert_eq!(
 6841            editor.text(cx),
 6842            "
 6843                {{{a}}}
 6844                {{{b}}}
 6845                {{{c}}}
 6846            "
 6847            .unindent()
 6848        );
 6849        assert_eq!(
 6850            editor.selections.display_ranges(cx),
 6851            [
 6852                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6853                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6854                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6855            ]
 6856        );
 6857
 6858        editor.undo(&Undo, window, cx);
 6859        editor.undo(&Undo, window, cx);
 6860        editor.undo(&Undo, window, cx);
 6861        assert_eq!(
 6862            editor.text(cx),
 6863            "
 6864                a
 6865                b
 6866                c
 6867            "
 6868            .unindent()
 6869        );
 6870        assert_eq!(
 6871            editor.selections.display_ranges(cx),
 6872            [
 6873                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6874                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6875                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6876            ]
 6877        );
 6878
 6879        // Ensure inserting the first character of a multi-byte bracket pair
 6880        // doesn't surround the selections with the bracket.
 6881        editor.handle_input("/", window, cx);
 6882        assert_eq!(
 6883            editor.text(cx),
 6884            "
 6885                /
 6886                /
 6887                /
 6888            "
 6889            .unindent()
 6890        );
 6891        assert_eq!(
 6892            editor.selections.display_ranges(cx),
 6893            [
 6894                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6895                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6896                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6897            ]
 6898        );
 6899
 6900        editor.undo(&Undo, window, cx);
 6901        assert_eq!(
 6902            editor.text(cx),
 6903            "
 6904                a
 6905                b
 6906                c
 6907            "
 6908            .unindent()
 6909        );
 6910        assert_eq!(
 6911            editor.selections.display_ranges(cx),
 6912            [
 6913                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6914                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6915                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6916            ]
 6917        );
 6918
 6919        // Ensure inserting the last character of a multi-byte bracket pair
 6920        // doesn't surround the selections with the bracket.
 6921        editor.handle_input("*", window, cx);
 6922        assert_eq!(
 6923            editor.text(cx),
 6924            "
 6925                *
 6926                *
 6927                *
 6928            "
 6929            .unindent()
 6930        );
 6931        assert_eq!(
 6932            editor.selections.display_ranges(cx),
 6933            [
 6934                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6935                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6936                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6937            ]
 6938        );
 6939    });
 6940}
 6941
 6942#[gpui::test]
 6943async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6944    init_test(cx, |_| {});
 6945
 6946    let language = Arc::new(Language::new(
 6947        LanguageConfig {
 6948            brackets: BracketPairConfig {
 6949                pairs: vec![BracketPair {
 6950                    start: "{".to_string(),
 6951                    end: "}".to_string(),
 6952                    close: true,
 6953                    surround: true,
 6954                    newline: true,
 6955                }],
 6956                ..Default::default()
 6957            },
 6958            autoclose_before: "}".to_string(),
 6959            ..Default::default()
 6960        },
 6961        Some(tree_sitter_rust::LANGUAGE.into()),
 6962    ));
 6963
 6964    let text = r#"
 6965        a
 6966        b
 6967        c
 6968    "#
 6969    .unindent();
 6970
 6971    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6972    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6973    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6974    editor
 6975        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6976        .await;
 6977
 6978    editor.update_in(cx, |editor, window, cx| {
 6979        editor.change_selections(None, window, cx, |s| {
 6980            s.select_ranges([
 6981                Point::new(0, 1)..Point::new(0, 1),
 6982                Point::new(1, 1)..Point::new(1, 1),
 6983                Point::new(2, 1)..Point::new(2, 1),
 6984            ])
 6985        });
 6986
 6987        editor.handle_input("{", window, cx);
 6988        editor.handle_input("{", window, cx);
 6989        editor.handle_input("_", window, cx);
 6990        assert_eq!(
 6991            editor.text(cx),
 6992            "
 6993                a{{_}}
 6994                b{{_}}
 6995                c{{_}}
 6996            "
 6997            .unindent()
 6998        );
 6999        assert_eq!(
 7000            editor.selections.ranges::<Point>(cx),
 7001            [
 7002                Point::new(0, 4)..Point::new(0, 4),
 7003                Point::new(1, 4)..Point::new(1, 4),
 7004                Point::new(2, 4)..Point::new(2, 4)
 7005            ]
 7006        );
 7007
 7008        editor.backspace(&Default::default(), window, cx);
 7009        editor.backspace(&Default::default(), window, cx);
 7010        assert_eq!(
 7011            editor.text(cx),
 7012            "
 7013                a{}
 7014                b{}
 7015                c{}
 7016            "
 7017            .unindent()
 7018        );
 7019        assert_eq!(
 7020            editor.selections.ranges::<Point>(cx),
 7021            [
 7022                Point::new(0, 2)..Point::new(0, 2),
 7023                Point::new(1, 2)..Point::new(1, 2),
 7024                Point::new(2, 2)..Point::new(2, 2)
 7025            ]
 7026        );
 7027
 7028        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7029        assert_eq!(
 7030            editor.text(cx),
 7031            "
 7032                a
 7033                b
 7034                c
 7035            "
 7036            .unindent()
 7037        );
 7038        assert_eq!(
 7039            editor.selections.ranges::<Point>(cx),
 7040            [
 7041                Point::new(0, 1)..Point::new(0, 1),
 7042                Point::new(1, 1)..Point::new(1, 1),
 7043                Point::new(2, 1)..Point::new(2, 1)
 7044            ]
 7045        );
 7046    });
 7047}
 7048
 7049#[gpui::test]
 7050async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7051    init_test(cx, |settings| {
 7052        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7053    });
 7054
 7055    let mut cx = EditorTestContext::new(cx).await;
 7056
 7057    let language = Arc::new(Language::new(
 7058        LanguageConfig {
 7059            brackets: BracketPairConfig {
 7060                pairs: vec![
 7061                    BracketPair {
 7062                        start: "{".to_string(),
 7063                        end: "}".to_string(),
 7064                        close: true,
 7065                        surround: true,
 7066                        newline: true,
 7067                    },
 7068                    BracketPair {
 7069                        start: "(".to_string(),
 7070                        end: ")".to_string(),
 7071                        close: true,
 7072                        surround: true,
 7073                        newline: true,
 7074                    },
 7075                    BracketPair {
 7076                        start: "[".to_string(),
 7077                        end: "]".to_string(),
 7078                        close: false,
 7079                        surround: true,
 7080                        newline: true,
 7081                    },
 7082                ],
 7083                ..Default::default()
 7084            },
 7085            autoclose_before: "})]".to_string(),
 7086            ..Default::default()
 7087        },
 7088        Some(tree_sitter_rust::LANGUAGE.into()),
 7089    ));
 7090
 7091    cx.language_registry().add(language.clone());
 7092    cx.update_buffer(|buffer, cx| {
 7093        buffer.set_language(Some(language), cx);
 7094    });
 7095
 7096    cx.set_state(
 7097        &"
 7098            {(ˇ)}
 7099            [[ˇ]]
 7100            {(ˇ)}
 7101        "
 7102        .unindent(),
 7103    );
 7104
 7105    cx.update_editor(|editor, window, cx| {
 7106        editor.backspace(&Default::default(), window, cx);
 7107        editor.backspace(&Default::default(), window, cx);
 7108    });
 7109
 7110    cx.assert_editor_state(
 7111        &"
 7112            ˇ
 7113            ˇ]]
 7114            ˇ
 7115        "
 7116        .unindent(),
 7117    );
 7118
 7119    cx.update_editor(|editor, window, cx| {
 7120        editor.handle_input("{", window, cx);
 7121        editor.handle_input("{", window, cx);
 7122        editor.move_right(&MoveRight, window, cx);
 7123        editor.move_right(&MoveRight, window, cx);
 7124        editor.move_left(&MoveLeft, window, cx);
 7125        editor.move_left(&MoveLeft, window, cx);
 7126        editor.backspace(&Default::default(), window, cx);
 7127    });
 7128
 7129    cx.assert_editor_state(
 7130        &"
 7131            {ˇ}
 7132            {ˇ}]]
 7133            {ˇ}
 7134        "
 7135        .unindent(),
 7136    );
 7137
 7138    cx.update_editor(|editor, window, cx| {
 7139        editor.backspace(&Default::default(), window, cx);
 7140    });
 7141
 7142    cx.assert_editor_state(
 7143        &"
 7144            ˇ
 7145            ˇ]]
 7146            ˇ
 7147        "
 7148        .unindent(),
 7149    );
 7150}
 7151
 7152#[gpui::test]
 7153async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7154    init_test(cx, |_| {});
 7155
 7156    let language = Arc::new(Language::new(
 7157        LanguageConfig::default(),
 7158        Some(tree_sitter_rust::LANGUAGE.into()),
 7159    ));
 7160
 7161    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7162    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7163    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7164    editor
 7165        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7166        .await;
 7167
 7168    editor.update_in(cx, |editor, window, cx| {
 7169        editor.set_auto_replace_emoji_shortcode(true);
 7170
 7171        editor.handle_input("Hello ", window, cx);
 7172        editor.handle_input(":wave", window, cx);
 7173        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7174
 7175        editor.handle_input(":", window, cx);
 7176        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7177
 7178        editor.handle_input(" :smile", window, cx);
 7179        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7180
 7181        editor.handle_input(":", window, cx);
 7182        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7183
 7184        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7185        editor.handle_input(":wave", window, cx);
 7186        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7187
 7188        editor.handle_input(":", window, cx);
 7189        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7190
 7191        editor.handle_input(":1", window, cx);
 7192        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7193
 7194        editor.handle_input(":", window, cx);
 7195        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7196
 7197        // Ensure shortcode does not get replaced when it is part of a word
 7198        editor.handle_input(" Test:wave", window, cx);
 7199        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7200
 7201        editor.handle_input(":", window, cx);
 7202        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7203
 7204        editor.set_auto_replace_emoji_shortcode(false);
 7205
 7206        // Ensure shortcode does not get replaced when auto replace is off
 7207        editor.handle_input(" :wave", window, cx);
 7208        assert_eq!(
 7209            editor.text(cx),
 7210            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7211        );
 7212
 7213        editor.handle_input(":", window, cx);
 7214        assert_eq!(
 7215            editor.text(cx),
 7216            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7217        );
 7218    });
 7219}
 7220
 7221#[gpui::test]
 7222async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7223    init_test(cx, |_| {});
 7224
 7225    let (text, insertion_ranges) = marked_text_ranges(
 7226        indoc! {"
 7227            ˇ
 7228        "},
 7229        false,
 7230    );
 7231
 7232    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7233    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7234
 7235    _ = editor.update_in(cx, |editor, window, cx| {
 7236        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7237
 7238        editor
 7239            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7240            .unwrap();
 7241
 7242        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7243            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7244            assert_eq!(editor.text(cx), expected_text);
 7245            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7246        }
 7247
 7248        assert(
 7249            editor,
 7250            cx,
 7251            indoc! {"
 7252            type «» =•
 7253            "},
 7254        );
 7255
 7256        assert!(editor.context_menu_visible(), "There should be a matches");
 7257    });
 7258}
 7259
 7260#[gpui::test]
 7261async fn test_snippets(cx: &mut TestAppContext) {
 7262    init_test(cx, |_| {});
 7263
 7264    let (text, insertion_ranges) = marked_text_ranges(
 7265        indoc! {"
 7266            a.ˇ b
 7267            a.ˇ b
 7268            a.ˇ b
 7269        "},
 7270        false,
 7271    );
 7272
 7273    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7274    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7275
 7276    editor.update_in(cx, |editor, window, cx| {
 7277        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7278
 7279        editor
 7280            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7281            .unwrap();
 7282
 7283        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7284            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7285            assert_eq!(editor.text(cx), expected_text);
 7286            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7287        }
 7288
 7289        assert(
 7290            editor,
 7291            cx,
 7292            indoc! {"
 7293                a.f(«one», two, «three») b
 7294                a.f(«one», two, «three») b
 7295                a.f(«one», two, «three») b
 7296            "},
 7297        );
 7298
 7299        // Can't move earlier than the first tab stop
 7300        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7301        assert(
 7302            editor,
 7303            cx,
 7304            indoc! {"
 7305                a.f(«one», two, «three») b
 7306                a.f(«one», two, «three») b
 7307                a.f(«one», two, «three») b
 7308            "},
 7309        );
 7310
 7311        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7312        assert(
 7313            editor,
 7314            cx,
 7315            indoc! {"
 7316                a.f(one, «two», three) b
 7317                a.f(one, «two», three) b
 7318                a.f(one, «two», three) b
 7319            "},
 7320        );
 7321
 7322        editor.move_to_prev_snippet_tabstop(window, cx);
 7323        assert(
 7324            editor,
 7325            cx,
 7326            indoc! {"
 7327                a.f(«one», two, «three») b
 7328                a.f(«one», two, «three») b
 7329                a.f(«one», two, «three») b
 7330            "},
 7331        );
 7332
 7333        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7334        assert(
 7335            editor,
 7336            cx,
 7337            indoc! {"
 7338                a.f(one, «two», three) b
 7339                a.f(one, «two», three) b
 7340                a.f(one, «two», three) b
 7341            "},
 7342        );
 7343        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7344        assert(
 7345            editor,
 7346            cx,
 7347            indoc! {"
 7348                a.f(one, two, three)ˇ b
 7349                a.f(one, two, three)ˇ b
 7350                a.f(one, two, three)ˇ b
 7351            "},
 7352        );
 7353
 7354        // As soon as the last tab stop is reached, snippet state is gone
 7355        editor.move_to_prev_snippet_tabstop(window, cx);
 7356        assert(
 7357            editor,
 7358            cx,
 7359            indoc! {"
 7360                a.f(one, two, three)ˇ b
 7361                a.f(one, two, three)ˇ b
 7362                a.f(one, two, three)ˇ b
 7363            "},
 7364        );
 7365    });
 7366}
 7367
 7368#[gpui::test]
 7369async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7370    init_test(cx, |_| {});
 7371
 7372    let fs = FakeFs::new(cx.executor());
 7373    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7374
 7375    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7376
 7377    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7378    language_registry.add(rust_lang());
 7379    let mut fake_servers = language_registry.register_fake_lsp(
 7380        "Rust",
 7381        FakeLspAdapter {
 7382            capabilities: lsp::ServerCapabilities {
 7383                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7384                ..Default::default()
 7385            },
 7386            ..Default::default()
 7387        },
 7388    );
 7389
 7390    let buffer = project
 7391        .update(cx, |project, cx| {
 7392            project.open_local_buffer(path!("/file.rs"), cx)
 7393        })
 7394        .await
 7395        .unwrap();
 7396
 7397    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7398    let (editor, cx) = cx.add_window_view(|window, cx| {
 7399        build_editor_with_project(project.clone(), buffer, window, cx)
 7400    });
 7401    editor.update_in(cx, |editor, window, cx| {
 7402        editor.set_text("one\ntwo\nthree\n", window, cx)
 7403    });
 7404    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7405
 7406    cx.executor().start_waiting();
 7407    let fake_server = fake_servers.next().await.unwrap();
 7408
 7409    let save = editor
 7410        .update_in(cx, |editor, window, cx| {
 7411            editor.save(true, project.clone(), window, cx)
 7412        })
 7413        .unwrap();
 7414    fake_server
 7415        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7416            assert_eq!(
 7417                params.text_document.uri,
 7418                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7419            );
 7420            assert_eq!(params.options.tab_size, 4);
 7421            Ok(Some(vec![lsp::TextEdit::new(
 7422                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7423                ", ".to_string(),
 7424            )]))
 7425        })
 7426        .next()
 7427        .await;
 7428    cx.executor().start_waiting();
 7429    save.await;
 7430
 7431    assert_eq!(
 7432        editor.update(cx, |editor, cx| editor.text(cx)),
 7433        "one, two\nthree\n"
 7434    );
 7435    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7436
 7437    editor.update_in(cx, |editor, window, cx| {
 7438        editor.set_text("one\ntwo\nthree\n", window, cx)
 7439    });
 7440    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7441
 7442    // Ensure we can still save even if formatting hangs.
 7443    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7444        assert_eq!(
 7445            params.text_document.uri,
 7446            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7447        );
 7448        futures::future::pending::<()>().await;
 7449        unreachable!()
 7450    });
 7451    let save = editor
 7452        .update_in(cx, |editor, window, cx| {
 7453            editor.save(true, project.clone(), window, cx)
 7454        })
 7455        .unwrap();
 7456    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7457    cx.executor().start_waiting();
 7458    save.await;
 7459    assert_eq!(
 7460        editor.update(cx, |editor, cx| editor.text(cx)),
 7461        "one\ntwo\nthree\n"
 7462    );
 7463    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7464
 7465    // For non-dirty buffer, no formatting request should be sent
 7466    let save = editor
 7467        .update_in(cx, |editor, window, cx| {
 7468            editor.save(true, project.clone(), window, cx)
 7469        })
 7470        .unwrap();
 7471    let _pending_format_request = fake_server
 7472        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7473            panic!("Should not be invoked on non-dirty buffer");
 7474        })
 7475        .next();
 7476    cx.executor().start_waiting();
 7477    save.await;
 7478
 7479    // Set rust language override and assert overridden tabsize is sent to language server
 7480    update_test_language_settings(cx, |settings| {
 7481        settings.languages.insert(
 7482            "Rust".into(),
 7483            LanguageSettingsContent {
 7484                tab_size: NonZeroU32::new(8),
 7485                ..Default::default()
 7486            },
 7487        );
 7488    });
 7489
 7490    editor.update_in(cx, |editor, window, cx| {
 7491        editor.set_text("somehting_new\n", window, cx)
 7492    });
 7493    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7494    let save = editor
 7495        .update_in(cx, |editor, window, cx| {
 7496            editor.save(true, project.clone(), window, cx)
 7497        })
 7498        .unwrap();
 7499    fake_server
 7500        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7501            assert_eq!(
 7502                params.text_document.uri,
 7503                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7504            );
 7505            assert_eq!(params.options.tab_size, 8);
 7506            Ok(Some(vec![]))
 7507        })
 7508        .next()
 7509        .await;
 7510    cx.executor().start_waiting();
 7511    save.await;
 7512}
 7513
 7514#[gpui::test]
 7515async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7516    init_test(cx, |_| {});
 7517
 7518    let cols = 4;
 7519    let rows = 10;
 7520    let sample_text_1 = sample_text(rows, cols, 'a');
 7521    assert_eq!(
 7522        sample_text_1,
 7523        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7524    );
 7525    let sample_text_2 = sample_text(rows, cols, 'l');
 7526    assert_eq!(
 7527        sample_text_2,
 7528        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7529    );
 7530    let sample_text_3 = sample_text(rows, cols, 'v');
 7531    assert_eq!(
 7532        sample_text_3,
 7533        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7534    );
 7535
 7536    let fs = FakeFs::new(cx.executor());
 7537    fs.insert_tree(
 7538        path!("/a"),
 7539        json!({
 7540            "main.rs": sample_text_1,
 7541            "other.rs": sample_text_2,
 7542            "lib.rs": sample_text_3,
 7543        }),
 7544    )
 7545    .await;
 7546
 7547    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7548    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7549    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7550
 7551    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7552    language_registry.add(rust_lang());
 7553    let mut fake_servers = language_registry.register_fake_lsp(
 7554        "Rust",
 7555        FakeLspAdapter {
 7556            capabilities: lsp::ServerCapabilities {
 7557                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7558                ..Default::default()
 7559            },
 7560            ..Default::default()
 7561        },
 7562    );
 7563
 7564    let worktree = project.update(cx, |project, cx| {
 7565        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7566        assert_eq!(worktrees.len(), 1);
 7567        worktrees.pop().unwrap()
 7568    });
 7569    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7570
 7571    let buffer_1 = project
 7572        .update(cx, |project, cx| {
 7573            project.open_buffer((worktree_id, "main.rs"), cx)
 7574        })
 7575        .await
 7576        .unwrap();
 7577    let buffer_2 = project
 7578        .update(cx, |project, cx| {
 7579            project.open_buffer((worktree_id, "other.rs"), cx)
 7580        })
 7581        .await
 7582        .unwrap();
 7583    let buffer_3 = project
 7584        .update(cx, |project, cx| {
 7585            project.open_buffer((worktree_id, "lib.rs"), cx)
 7586        })
 7587        .await
 7588        .unwrap();
 7589
 7590    let multi_buffer = cx.new(|cx| {
 7591        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7592        multi_buffer.push_excerpts(
 7593            buffer_1.clone(),
 7594            [
 7595                ExcerptRange {
 7596                    context: Point::new(0, 0)..Point::new(3, 0),
 7597                    primary: None,
 7598                },
 7599                ExcerptRange {
 7600                    context: Point::new(5, 0)..Point::new(7, 0),
 7601                    primary: None,
 7602                },
 7603                ExcerptRange {
 7604                    context: Point::new(9, 0)..Point::new(10, 4),
 7605                    primary: None,
 7606                },
 7607            ],
 7608            cx,
 7609        );
 7610        multi_buffer.push_excerpts(
 7611            buffer_2.clone(),
 7612            [
 7613                ExcerptRange {
 7614                    context: Point::new(0, 0)..Point::new(3, 0),
 7615                    primary: None,
 7616                },
 7617                ExcerptRange {
 7618                    context: Point::new(5, 0)..Point::new(7, 0),
 7619                    primary: None,
 7620                },
 7621                ExcerptRange {
 7622                    context: Point::new(9, 0)..Point::new(10, 4),
 7623                    primary: None,
 7624                },
 7625            ],
 7626            cx,
 7627        );
 7628        multi_buffer.push_excerpts(
 7629            buffer_3.clone(),
 7630            [
 7631                ExcerptRange {
 7632                    context: Point::new(0, 0)..Point::new(3, 0),
 7633                    primary: None,
 7634                },
 7635                ExcerptRange {
 7636                    context: Point::new(5, 0)..Point::new(7, 0),
 7637                    primary: None,
 7638                },
 7639                ExcerptRange {
 7640                    context: Point::new(9, 0)..Point::new(10, 4),
 7641                    primary: None,
 7642                },
 7643            ],
 7644            cx,
 7645        );
 7646        multi_buffer
 7647    });
 7648    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7649        Editor::new(
 7650            EditorMode::Full,
 7651            multi_buffer,
 7652            Some(project.clone()),
 7653            true,
 7654            window,
 7655            cx,
 7656        )
 7657    });
 7658
 7659    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7660        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7661            s.select_ranges(Some(1..2))
 7662        });
 7663        editor.insert("|one|two|three|", window, cx);
 7664    });
 7665    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7666    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7667        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7668            s.select_ranges(Some(60..70))
 7669        });
 7670        editor.insert("|four|five|six|", window, cx);
 7671    });
 7672    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7673
 7674    // First two buffers should be edited, but not the third one.
 7675    assert_eq!(
 7676        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7677        "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}",
 7678    );
 7679    buffer_1.update(cx, |buffer, _| {
 7680        assert!(buffer.is_dirty());
 7681        assert_eq!(
 7682            buffer.text(),
 7683            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7684        )
 7685    });
 7686    buffer_2.update(cx, |buffer, _| {
 7687        assert!(buffer.is_dirty());
 7688        assert_eq!(
 7689            buffer.text(),
 7690            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7691        )
 7692    });
 7693    buffer_3.update(cx, |buffer, _| {
 7694        assert!(!buffer.is_dirty());
 7695        assert_eq!(buffer.text(), sample_text_3,)
 7696    });
 7697    cx.executor().run_until_parked();
 7698
 7699    cx.executor().start_waiting();
 7700    let save = multi_buffer_editor
 7701        .update_in(cx, |editor, window, cx| {
 7702            editor.save(true, project.clone(), window, cx)
 7703        })
 7704        .unwrap();
 7705
 7706    let fake_server = fake_servers.next().await.unwrap();
 7707    fake_server
 7708        .server
 7709        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7710            Ok(Some(vec![lsp::TextEdit::new(
 7711                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7712                format!("[{} formatted]", params.text_document.uri),
 7713            )]))
 7714        })
 7715        .detach();
 7716    save.await;
 7717
 7718    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7719    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7720    assert_eq!(
 7721        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7722        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}"),
 7723    );
 7724    buffer_1.update(cx, |buffer, _| {
 7725        assert!(!buffer.is_dirty());
 7726        assert_eq!(
 7727            buffer.text(),
 7728            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7729        )
 7730    });
 7731    buffer_2.update(cx, |buffer, _| {
 7732        assert!(!buffer.is_dirty());
 7733        assert_eq!(
 7734            buffer.text(),
 7735            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7736        )
 7737    });
 7738    buffer_3.update(cx, |buffer, _| {
 7739        assert!(!buffer.is_dirty());
 7740        assert_eq!(buffer.text(), sample_text_3,)
 7741    });
 7742}
 7743
 7744#[gpui::test]
 7745async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7746    init_test(cx, |_| {});
 7747
 7748    let fs = FakeFs::new(cx.executor());
 7749    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7750
 7751    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7752
 7753    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7754    language_registry.add(rust_lang());
 7755    let mut fake_servers = language_registry.register_fake_lsp(
 7756        "Rust",
 7757        FakeLspAdapter {
 7758            capabilities: lsp::ServerCapabilities {
 7759                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7760                ..Default::default()
 7761            },
 7762            ..Default::default()
 7763        },
 7764    );
 7765
 7766    let buffer = project
 7767        .update(cx, |project, cx| {
 7768            project.open_local_buffer(path!("/file.rs"), cx)
 7769        })
 7770        .await
 7771        .unwrap();
 7772
 7773    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7774    let (editor, cx) = cx.add_window_view(|window, cx| {
 7775        build_editor_with_project(project.clone(), buffer, window, cx)
 7776    });
 7777    editor.update_in(cx, |editor, window, cx| {
 7778        editor.set_text("one\ntwo\nthree\n", window, cx)
 7779    });
 7780    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7781
 7782    cx.executor().start_waiting();
 7783    let fake_server = fake_servers.next().await.unwrap();
 7784
 7785    let save = editor
 7786        .update_in(cx, |editor, window, cx| {
 7787            editor.save(true, project.clone(), window, cx)
 7788        })
 7789        .unwrap();
 7790    fake_server
 7791        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7792            assert_eq!(
 7793                params.text_document.uri,
 7794                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7795            );
 7796            assert_eq!(params.options.tab_size, 4);
 7797            Ok(Some(vec![lsp::TextEdit::new(
 7798                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7799                ", ".to_string(),
 7800            )]))
 7801        })
 7802        .next()
 7803        .await;
 7804    cx.executor().start_waiting();
 7805    save.await;
 7806    assert_eq!(
 7807        editor.update(cx, |editor, cx| editor.text(cx)),
 7808        "one, two\nthree\n"
 7809    );
 7810    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7811
 7812    editor.update_in(cx, |editor, window, cx| {
 7813        editor.set_text("one\ntwo\nthree\n", window, cx)
 7814    });
 7815    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7816
 7817    // Ensure we can still save even if formatting hangs.
 7818    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7819        move |params, _| async move {
 7820            assert_eq!(
 7821                params.text_document.uri,
 7822                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7823            );
 7824            futures::future::pending::<()>().await;
 7825            unreachable!()
 7826        },
 7827    );
 7828    let save = editor
 7829        .update_in(cx, |editor, window, cx| {
 7830            editor.save(true, project.clone(), window, cx)
 7831        })
 7832        .unwrap();
 7833    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7834    cx.executor().start_waiting();
 7835    save.await;
 7836    assert_eq!(
 7837        editor.update(cx, |editor, cx| editor.text(cx)),
 7838        "one\ntwo\nthree\n"
 7839    );
 7840    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7841
 7842    // For non-dirty buffer, no formatting request should be sent
 7843    let save = editor
 7844        .update_in(cx, |editor, window, cx| {
 7845            editor.save(true, project.clone(), window, cx)
 7846        })
 7847        .unwrap();
 7848    let _pending_format_request = fake_server
 7849        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7850            panic!("Should not be invoked on non-dirty buffer");
 7851        })
 7852        .next();
 7853    cx.executor().start_waiting();
 7854    save.await;
 7855
 7856    // Set Rust language override and assert overridden tabsize is sent to language server
 7857    update_test_language_settings(cx, |settings| {
 7858        settings.languages.insert(
 7859            "Rust".into(),
 7860            LanguageSettingsContent {
 7861                tab_size: NonZeroU32::new(8),
 7862                ..Default::default()
 7863            },
 7864        );
 7865    });
 7866
 7867    editor.update_in(cx, |editor, window, cx| {
 7868        editor.set_text("somehting_new\n", window, cx)
 7869    });
 7870    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7871    let save = editor
 7872        .update_in(cx, |editor, window, cx| {
 7873            editor.save(true, project.clone(), window, cx)
 7874        })
 7875        .unwrap();
 7876    fake_server
 7877        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7878            assert_eq!(
 7879                params.text_document.uri,
 7880                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7881            );
 7882            assert_eq!(params.options.tab_size, 8);
 7883            Ok(Some(vec![]))
 7884        })
 7885        .next()
 7886        .await;
 7887    cx.executor().start_waiting();
 7888    save.await;
 7889}
 7890
 7891#[gpui::test]
 7892async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7893    init_test(cx, |settings| {
 7894        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7895            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7896        ))
 7897    });
 7898
 7899    let fs = FakeFs::new(cx.executor());
 7900    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7901
 7902    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7903
 7904    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7905    language_registry.add(Arc::new(Language::new(
 7906        LanguageConfig {
 7907            name: "Rust".into(),
 7908            matcher: LanguageMatcher {
 7909                path_suffixes: vec!["rs".to_string()],
 7910                ..Default::default()
 7911            },
 7912            ..LanguageConfig::default()
 7913        },
 7914        Some(tree_sitter_rust::LANGUAGE.into()),
 7915    )));
 7916    update_test_language_settings(cx, |settings| {
 7917        // Enable Prettier formatting for the same buffer, and ensure
 7918        // LSP is called instead of Prettier.
 7919        settings.defaults.prettier = Some(PrettierSettings {
 7920            allowed: true,
 7921            ..PrettierSettings::default()
 7922        });
 7923    });
 7924    let mut fake_servers = language_registry.register_fake_lsp(
 7925        "Rust",
 7926        FakeLspAdapter {
 7927            capabilities: lsp::ServerCapabilities {
 7928                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7929                ..Default::default()
 7930            },
 7931            ..Default::default()
 7932        },
 7933    );
 7934
 7935    let buffer = project
 7936        .update(cx, |project, cx| {
 7937            project.open_local_buffer(path!("/file.rs"), cx)
 7938        })
 7939        .await
 7940        .unwrap();
 7941
 7942    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7943    let (editor, cx) = cx.add_window_view(|window, cx| {
 7944        build_editor_with_project(project.clone(), buffer, window, cx)
 7945    });
 7946    editor.update_in(cx, |editor, window, cx| {
 7947        editor.set_text("one\ntwo\nthree\n", window, cx)
 7948    });
 7949
 7950    cx.executor().start_waiting();
 7951    let fake_server = fake_servers.next().await.unwrap();
 7952
 7953    let format = editor
 7954        .update_in(cx, |editor, window, cx| {
 7955            editor.perform_format(
 7956                project.clone(),
 7957                FormatTrigger::Manual,
 7958                FormatTarget::Buffers,
 7959                window,
 7960                cx,
 7961            )
 7962        })
 7963        .unwrap();
 7964    fake_server
 7965        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7966            assert_eq!(
 7967                params.text_document.uri,
 7968                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7969            );
 7970            assert_eq!(params.options.tab_size, 4);
 7971            Ok(Some(vec![lsp::TextEdit::new(
 7972                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7973                ", ".to_string(),
 7974            )]))
 7975        })
 7976        .next()
 7977        .await;
 7978    cx.executor().start_waiting();
 7979    format.await;
 7980    assert_eq!(
 7981        editor.update(cx, |editor, cx| editor.text(cx)),
 7982        "one, two\nthree\n"
 7983    );
 7984
 7985    editor.update_in(cx, |editor, window, cx| {
 7986        editor.set_text("one\ntwo\nthree\n", window, cx)
 7987    });
 7988    // Ensure we don't lock if formatting hangs.
 7989    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7990        assert_eq!(
 7991            params.text_document.uri,
 7992            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7993        );
 7994        futures::future::pending::<()>().await;
 7995        unreachable!()
 7996    });
 7997    let format = editor
 7998        .update_in(cx, |editor, window, cx| {
 7999            editor.perform_format(
 8000                project,
 8001                FormatTrigger::Manual,
 8002                FormatTarget::Buffers,
 8003                window,
 8004                cx,
 8005            )
 8006        })
 8007        .unwrap();
 8008    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8009    cx.executor().start_waiting();
 8010    format.await;
 8011    assert_eq!(
 8012        editor.update(cx, |editor, cx| editor.text(cx)),
 8013        "one\ntwo\nthree\n"
 8014    );
 8015}
 8016
 8017#[gpui::test]
 8018async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8019    init_test(cx, |settings| {
 8020        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8021            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8022        ))
 8023    });
 8024
 8025    let fs = FakeFs::new(cx.executor());
 8026    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8027
 8028    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8029
 8030    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8031    language_registry.add(Arc::new(Language::new(
 8032        LanguageConfig {
 8033            name: "TypeScript".into(),
 8034            matcher: LanguageMatcher {
 8035                path_suffixes: vec!["ts".to_string()],
 8036                ..Default::default()
 8037            },
 8038            ..LanguageConfig::default()
 8039        },
 8040        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8041    )));
 8042    update_test_language_settings(cx, |settings| {
 8043        settings.defaults.prettier = Some(PrettierSettings {
 8044            allowed: true,
 8045            ..PrettierSettings::default()
 8046        });
 8047    });
 8048    let mut fake_servers = language_registry.register_fake_lsp(
 8049        "TypeScript",
 8050        FakeLspAdapter {
 8051            capabilities: lsp::ServerCapabilities {
 8052                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8053                ..Default::default()
 8054            },
 8055            ..Default::default()
 8056        },
 8057    );
 8058
 8059    let buffer = project
 8060        .update(cx, |project, cx| {
 8061            project.open_local_buffer(path!("/file.ts"), cx)
 8062        })
 8063        .await
 8064        .unwrap();
 8065
 8066    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8067    let (editor, cx) = cx.add_window_view(|window, cx| {
 8068        build_editor_with_project(project.clone(), buffer, window, cx)
 8069    });
 8070    editor.update_in(cx, |editor, window, cx| {
 8071        editor.set_text(
 8072            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8073            window,
 8074            cx,
 8075        )
 8076    });
 8077
 8078    cx.executor().start_waiting();
 8079    let fake_server = fake_servers.next().await.unwrap();
 8080
 8081    let format = editor
 8082        .update_in(cx, |editor, window, cx| {
 8083            editor.perform_code_action_kind(
 8084                project.clone(),
 8085                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8086                window,
 8087                cx,
 8088            )
 8089        })
 8090        .unwrap();
 8091    fake_server
 8092        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8093            assert_eq!(
 8094                params.text_document.uri,
 8095                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8096            );
 8097            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8098                lsp::CodeAction {
 8099                    title: "Organize Imports".to_string(),
 8100                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8101                    edit: Some(lsp::WorkspaceEdit {
 8102                        changes: Some(
 8103                            [(
 8104                                params.text_document.uri.clone(),
 8105                                vec![lsp::TextEdit::new(
 8106                                    lsp::Range::new(
 8107                                        lsp::Position::new(1, 0),
 8108                                        lsp::Position::new(2, 0),
 8109                                    ),
 8110                                    "".to_string(),
 8111                                )],
 8112                            )]
 8113                            .into_iter()
 8114                            .collect(),
 8115                        ),
 8116                        ..Default::default()
 8117                    }),
 8118                    ..Default::default()
 8119                },
 8120            )]))
 8121        })
 8122        .next()
 8123        .await;
 8124    cx.executor().start_waiting();
 8125    format.await;
 8126    assert_eq!(
 8127        editor.update(cx, |editor, cx| editor.text(cx)),
 8128        "import { a } from 'module';\n\nconst x = a;\n"
 8129    );
 8130
 8131    editor.update_in(cx, |editor, window, cx| {
 8132        editor.set_text(
 8133            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8134            window,
 8135            cx,
 8136        )
 8137    });
 8138    // Ensure we don't lock if code action hangs.
 8139    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8140        move |params, _| async move {
 8141            assert_eq!(
 8142                params.text_document.uri,
 8143                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8144            );
 8145            futures::future::pending::<()>().await;
 8146            unreachable!()
 8147        },
 8148    );
 8149    let format = editor
 8150        .update_in(cx, |editor, window, cx| {
 8151            editor.perform_code_action_kind(
 8152                project,
 8153                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8154                window,
 8155                cx,
 8156            )
 8157        })
 8158        .unwrap();
 8159    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8160    cx.executor().start_waiting();
 8161    format.await;
 8162    assert_eq!(
 8163        editor.update(cx, |editor, cx| editor.text(cx)),
 8164        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8165    );
 8166}
 8167
 8168#[gpui::test]
 8169async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8170    init_test(cx, |_| {});
 8171
 8172    let mut cx = EditorLspTestContext::new_rust(
 8173        lsp::ServerCapabilities {
 8174            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8175            ..Default::default()
 8176        },
 8177        cx,
 8178    )
 8179    .await;
 8180
 8181    cx.set_state(indoc! {"
 8182        one.twoˇ
 8183    "});
 8184
 8185    // The format request takes a long time. When it completes, it inserts
 8186    // a newline and an indent before the `.`
 8187    cx.lsp
 8188        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8189            let executor = cx.background_executor().clone();
 8190            async move {
 8191                executor.timer(Duration::from_millis(100)).await;
 8192                Ok(Some(vec![lsp::TextEdit {
 8193                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8194                    new_text: "\n    ".into(),
 8195                }]))
 8196            }
 8197        });
 8198
 8199    // Submit a format request.
 8200    let format_1 = cx
 8201        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8202        .unwrap();
 8203    cx.executor().run_until_parked();
 8204
 8205    // Submit a second format request.
 8206    let format_2 = cx
 8207        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8208        .unwrap();
 8209    cx.executor().run_until_parked();
 8210
 8211    // Wait for both format requests to complete
 8212    cx.executor().advance_clock(Duration::from_millis(200));
 8213    cx.executor().start_waiting();
 8214    format_1.await.unwrap();
 8215    cx.executor().start_waiting();
 8216    format_2.await.unwrap();
 8217
 8218    // The formatting edits only happens once.
 8219    cx.assert_editor_state(indoc! {"
 8220        one
 8221            .twoˇ
 8222    "});
 8223}
 8224
 8225#[gpui::test]
 8226async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8227    init_test(cx, |settings| {
 8228        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8229    });
 8230
 8231    let mut cx = EditorLspTestContext::new_rust(
 8232        lsp::ServerCapabilities {
 8233            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8234            ..Default::default()
 8235        },
 8236        cx,
 8237    )
 8238    .await;
 8239
 8240    // Set up a buffer white some trailing whitespace and no trailing newline.
 8241    cx.set_state(
 8242        &[
 8243            "one ",   //
 8244            "twoˇ",   //
 8245            "three ", //
 8246            "four",   //
 8247        ]
 8248        .join("\n"),
 8249    );
 8250
 8251    // Submit a format request.
 8252    let format = cx
 8253        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8254        .unwrap();
 8255
 8256    // Record which buffer changes have been sent to the language server
 8257    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8258    cx.lsp
 8259        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8260            let buffer_changes = buffer_changes.clone();
 8261            move |params, _| {
 8262                buffer_changes.lock().extend(
 8263                    params
 8264                        .content_changes
 8265                        .into_iter()
 8266                        .map(|e| (e.range.unwrap(), e.text)),
 8267                );
 8268            }
 8269        });
 8270
 8271    // Handle formatting requests to the language server.
 8272    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8273        let buffer_changes = buffer_changes.clone();
 8274        move |_, _| {
 8275            // When formatting is requested, trailing whitespace has already been stripped,
 8276            // and the trailing newline has already been added.
 8277            assert_eq!(
 8278                &buffer_changes.lock()[1..],
 8279                &[
 8280                    (
 8281                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8282                        "".into()
 8283                    ),
 8284                    (
 8285                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8286                        "".into()
 8287                    ),
 8288                    (
 8289                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8290                        "\n".into()
 8291                    ),
 8292                ]
 8293            );
 8294
 8295            // Insert blank lines between each line of the buffer.
 8296            async move {
 8297                Ok(Some(vec![
 8298                    lsp::TextEdit {
 8299                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8300                        new_text: "\n".into(),
 8301                    },
 8302                    lsp::TextEdit {
 8303                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8304                        new_text: "\n".into(),
 8305                    },
 8306                ]))
 8307            }
 8308        }
 8309    });
 8310
 8311    // After formatting the buffer, the trailing whitespace is stripped,
 8312    // a newline is appended, and the edits provided by the language server
 8313    // have been applied.
 8314    format.await.unwrap();
 8315    cx.assert_editor_state(
 8316        &[
 8317            "one",   //
 8318            "",      //
 8319            "twoˇ",  //
 8320            "",      //
 8321            "three", //
 8322            "four",  //
 8323            "",      //
 8324        ]
 8325        .join("\n"),
 8326    );
 8327
 8328    // Undoing the formatting undoes the trailing whitespace removal, the
 8329    // trailing newline, and the LSP edits.
 8330    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8331    cx.assert_editor_state(
 8332        &[
 8333            "one ",   //
 8334            "twoˇ",   //
 8335            "three ", //
 8336            "four",   //
 8337        ]
 8338        .join("\n"),
 8339    );
 8340}
 8341
 8342#[gpui::test]
 8343async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8344    cx: &mut TestAppContext,
 8345) {
 8346    init_test(cx, |_| {});
 8347
 8348    cx.update(|cx| {
 8349        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8350            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8351                settings.auto_signature_help = Some(true);
 8352            });
 8353        });
 8354    });
 8355
 8356    let mut cx = EditorLspTestContext::new_rust(
 8357        lsp::ServerCapabilities {
 8358            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8359                ..Default::default()
 8360            }),
 8361            ..Default::default()
 8362        },
 8363        cx,
 8364    )
 8365    .await;
 8366
 8367    let language = Language::new(
 8368        LanguageConfig {
 8369            name: "Rust".into(),
 8370            brackets: BracketPairConfig {
 8371                pairs: vec![
 8372                    BracketPair {
 8373                        start: "{".to_string(),
 8374                        end: "}".to_string(),
 8375                        close: true,
 8376                        surround: true,
 8377                        newline: true,
 8378                    },
 8379                    BracketPair {
 8380                        start: "(".to_string(),
 8381                        end: ")".to_string(),
 8382                        close: true,
 8383                        surround: true,
 8384                        newline: true,
 8385                    },
 8386                    BracketPair {
 8387                        start: "/*".to_string(),
 8388                        end: " */".to_string(),
 8389                        close: true,
 8390                        surround: true,
 8391                        newline: true,
 8392                    },
 8393                    BracketPair {
 8394                        start: "[".to_string(),
 8395                        end: "]".to_string(),
 8396                        close: false,
 8397                        surround: false,
 8398                        newline: true,
 8399                    },
 8400                    BracketPair {
 8401                        start: "\"".to_string(),
 8402                        end: "\"".to_string(),
 8403                        close: true,
 8404                        surround: true,
 8405                        newline: false,
 8406                    },
 8407                    BracketPair {
 8408                        start: "<".to_string(),
 8409                        end: ">".to_string(),
 8410                        close: false,
 8411                        surround: true,
 8412                        newline: true,
 8413                    },
 8414                ],
 8415                ..Default::default()
 8416            },
 8417            autoclose_before: "})]".to_string(),
 8418            ..Default::default()
 8419        },
 8420        Some(tree_sitter_rust::LANGUAGE.into()),
 8421    );
 8422    let language = Arc::new(language);
 8423
 8424    cx.language_registry().add(language.clone());
 8425    cx.update_buffer(|buffer, cx| {
 8426        buffer.set_language(Some(language), cx);
 8427    });
 8428
 8429    cx.set_state(
 8430        &r#"
 8431            fn main() {
 8432                sampleˇ
 8433            }
 8434        "#
 8435        .unindent(),
 8436    );
 8437
 8438    cx.update_editor(|editor, window, cx| {
 8439        editor.handle_input("(", window, cx);
 8440    });
 8441    cx.assert_editor_state(
 8442        &"
 8443            fn main() {
 8444                sample(ˇ)
 8445            }
 8446        "
 8447        .unindent(),
 8448    );
 8449
 8450    let mocked_response = lsp::SignatureHelp {
 8451        signatures: vec![lsp::SignatureInformation {
 8452            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8453            documentation: None,
 8454            parameters: Some(vec![
 8455                lsp::ParameterInformation {
 8456                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8457                    documentation: None,
 8458                },
 8459                lsp::ParameterInformation {
 8460                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8461                    documentation: None,
 8462                },
 8463            ]),
 8464            active_parameter: None,
 8465        }],
 8466        active_signature: Some(0),
 8467        active_parameter: Some(0),
 8468    };
 8469    handle_signature_help_request(&mut cx, mocked_response).await;
 8470
 8471    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8472        .await;
 8473
 8474    cx.editor(|editor, _, _| {
 8475        let signature_help_state = editor.signature_help_state.popover().cloned();
 8476        assert_eq!(
 8477            signature_help_state.unwrap().label,
 8478            "param1: u8, param2: u8"
 8479        );
 8480    });
 8481}
 8482
 8483#[gpui::test]
 8484async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8485    init_test(cx, |_| {});
 8486
 8487    cx.update(|cx| {
 8488        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8489            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8490                settings.auto_signature_help = Some(false);
 8491                settings.show_signature_help_after_edits = Some(false);
 8492            });
 8493        });
 8494    });
 8495
 8496    let mut cx = EditorLspTestContext::new_rust(
 8497        lsp::ServerCapabilities {
 8498            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8499                ..Default::default()
 8500            }),
 8501            ..Default::default()
 8502        },
 8503        cx,
 8504    )
 8505    .await;
 8506
 8507    let language = Language::new(
 8508        LanguageConfig {
 8509            name: "Rust".into(),
 8510            brackets: BracketPairConfig {
 8511                pairs: vec![
 8512                    BracketPair {
 8513                        start: "{".to_string(),
 8514                        end: "}".to_string(),
 8515                        close: true,
 8516                        surround: true,
 8517                        newline: true,
 8518                    },
 8519                    BracketPair {
 8520                        start: "(".to_string(),
 8521                        end: ")".to_string(),
 8522                        close: true,
 8523                        surround: true,
 8524                        newline: true,
 8525                    },
 8526                    BracketPair {
 8527                        start: "/*".to_string(),
 8528                        end: " */".to_string(),
 8529                        close: true,
 8530                        surround: true,
 8531                        newline: true,
 8532                    },
 8533                    BracketPair {
 8534                        start: "[".to_string(),
 8535                        end: "]".to_string(),
 8536                        close: false,
 8537                        surround: false,
 8538                        newline: true,
 8539                    },
 8540                    BracketPair {
 8541                        start: "\"".to_string(),
 8542                        end: "\"".to_string(),
 8543                        close: true,
 8544                        surround: true,
 8545                        newline: false,
 8546                    },
 8547                    BracketPair {
 8548                        start: "<".to_string(),
 8549                        end: ">".to_string(),
 8550                        close: false,
 8551                        surround: true,
 8552                        newline: true,
 8553                    },
 8554                ],
 8555                ..Default::default()
 8556            },
 8557            autoclose_before: "})]".to_string(),
 8558            ..Default::default()
 8559        },
 8560        Some(tree_sitter_rust::LANGUAGE.into()),
 8561    );
 8562    let language = Arc::new(language);
 8563
 8564    cx.language_registry().add(language.clone());
 8565    cx.update_buffer(|buffer, cx| {
 8566        buffer.set_language(Some(language), cx);
 8567    });
 8568
 8569    // Ensure that signature_help is not called when no signature help is enabled.
 8570    cx.set_state(
 8571        &r#"
 8572            fn main() {
 8573                sampleˇ
 8574            }
 8575        "#
 8576        .unindent(),
 8577    );
 8578    cx.update_editor(|editor, window, cx| {
 8579        editor.handle_input("(", window, cx);
 8580    });
 8581    cx.assert_editor_state(
 8582        &"
 8583            fn main() {
 8584                sample(ˇ)
 8585            }
 8586        "
 8587        .unindent(),
 8588    );
 8589    cx.editor(|editor, _, _| {
 8590        assert!(editor.signature_help_state.task().is_none());
 8591    });
 8592
 8593    let mocked_response = lsp::SignatureHelp {
 8594        signatures: vec![lsp::SignatureInformation {
 8595            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8596            documentation: None,
 8597            parameters: Some(vec![
 8598                lsp::ParameterInformation {
 8599                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8600                    documentation: None,
 8601                },
 8602                lsp::ParameterInformation {
 8603                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8604                    documentation: None,
 8605                },
 8606            ]),
 8607            active_parameter: None,
 8608        }],
 8609        active_signature: Some(0),
 8610        active_parameter: Some(0),
 8611    };
 8612
 8613    // Ensure that signature_help is called when enabled afte edits
 8614    cx.update(|_, cx| {
 8615        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8616            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8617                settings.auto_signature_help = Some(false);
 8618                settings.show_signature_help_after_edits = Some(true);
 8619            });
 8620        });
 8621    });
 8622    cx.set_state(
 8623        &r#"
 8624            fn main() {
 8625                sampleˇ
 8626            }
 8627        "#
 8628        .unindent(),
 8629    );
 8630    cx.update_editor(|editor, window, cx| {
 8631        editor.handle_input("(", window, cx);
 8632    });
 8633    cx.assert_editor_state(
 8634        &"
 8635            fn main() {
 8636                sample(ˇ)
 8637            }
 8638        "
 8639        .unindent(),
 8640    );
 8641    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8642    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8643        .await;
 8644    cx.update_editor(|editor, _, _| {
 8645        let signature_help_state = editor.signature_help_state.popover().cloned();
 8646        assert!(signature_help_state.is_some());
 8647        assert_eq!(
 8648            signature_help_state.unwrap().label,
 8649            "param1: u8, param2: u8"
 8650        );
 8651        editor.signature_help_state = SignatureHelpState::default();
 8652    });
 8653
 8654    // Ensure that signature_help is called when auto signature help override is enabled
 8655    cx.update(|_, cx| {
 8656        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8657            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8658                settings.auto_signature_help = Some(true);
 8659                settings.show_signature_help_after_edits = Some(false);
 8660            });
 8661        });
 8662    });
 8663    cx.set_state(
 8664        &r#"
 8665            fn main() {
 8666                sampleˇ
 8667            }
 8668        "#
 8669        .unindent(),
 8670    );
 8671    cx.update_editor(|editor, window, cx| {
 8672        editor.handle_input("(", window, cx);
 8673    });
 8674    cx.assert_editor_state(
 8675        &"
 8676            fn main() {
 8677                sample(ˇ)
 8678            }
 8679        "
 8680        .unindent(),
 8681    );
 8682    handle_signature_help_request(&mut cx, mocked_response).await;
 8683    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8684        .await;
 8685    cx.editor(|editor, _, _| {
 8686        let signature_help_state = editor.signature_help_state.popover().cloned();
 8687        assert!(signature_help_state.is_some());
 8688        assert_eq!(
 8689            signature_help_state.unwrap().label,
 8690            "param1: u8, param2: u8"
 8691        );
 8692    });
 8693}
 8694
 8695#[gpui::test]
 8696async fn test_signature_help(cx: &mut TestAppContext) {
 8697    init_test(cx, |_| {});
 8698    cx.update(|cx| {
 8699        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8700            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8701                settings.auto_signature_help = Some(true);
 8702            });
 8703        });
 8704    });
 8705
 8706    let mut cx = EditorLspTestContext::new_rust(
 8707        lsp::ServerCapabilities {
 8708            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8709                ..Default::default()
 8710            }),
 8711            ..Default::default()
 8712        },
 8713        cx,
 8714    )
 8715    .await;
 8716
 8717    // A test that directly calls `show_signature_help`
 8718    cx.update_editor(|editor, window, cx| {
 8719        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8720    });
 8721
 8722    let mocked_response = lsp::SignatureHelp {
 8723        signatures: vec![lsp::SignatureInformation {
 8724            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8725            documentation: None,
 8726            parameters: Some(vec![
 8727                lsp::ParameterInformation {
 8728                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8729                    documentation: None,
 8730                },
 8731                lsp::ParameterInformation {
 8732                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8733                    documentation: None,
 8734                },
 8735            ]),
 8736            active_parameter: None,
 8737        }],
 8738        active_signature: Some(0),
 8739        active_parameter: Some(0),
 8740    };
 8741    handle_signature_help_request(&mut cx, mocked_response).await;
 8742
 8743    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8744        .await;
 8745
 8746    cx.editor(|editor, _, _| {
 8747        let signature_help_state = editor.signature_help_state.popover().cloned();
 8748        assert!(signature_help_state.is_some());
 8749        assert_eq!(
 8750            signature_help_state.unwrap().label,
 8751            "param1: u8, param2: u8"
 8752        );
 8753    });
 8754
 8755    // When exiting outside from inside the brackets, `signature_help` is closed.
 8756    cx.set_state(indoc! {"
 8757        fn main() {
 8758            sample(ˇ);
 8759        }
 8760
 8761        fn sample(param1: u8, param2: u8) {}
 8762    "});
 8763
 8764    cx.update_editor(|editor, window, cx| {
 8765        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8766    });
 8767
 8768    let mocked_response = lsp::SignatureHelp {
 8769        signatures: Vec::new(),
 8770        active_signature: None,
 8771        active_parameter: None,
 8772    };
 8773    handle_signature_help_request(&mut cx, mocked_response).await;
 8774
 8775    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8776        .await;
 8777
 8778    cx.editor(|editor, _, _| {
 8779        assert!(!editor.signature_help_state.is_shown());
 8780    });
 8781
 8782    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8783    cx.set_state(indoc! {"
 8784        fn main() {
 8785            sample(ˇ);
 8786        }
 8787
 8788        fn sample(param1: u8, param2: u8) {}
 8789    "});
 8790
 8791    let mocked_response = lsp::SignatureHelp {
 8792        signatures: vec![lsp::SignatureInformation {
 8793            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8794            documentation: None,
 8795            parameters: Some(vec![
 8796                lsp::ParameterInformation {
 8797                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8798                    documentation: None,
 8799                },
 8800                lsp::ParameterInformation {
 8801                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8802                    documentation: None,
 8803                },
 8804            ]),
 8805            active_parameter: None,
 8806        }],
 8807        active_signature: Some(0),
 8808        active_parameter: Some(0),
 8809    };
 8810    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8811    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8812        .await;
 8813    cx.editor(|editor, _, _| {
 8814        assert!(editor.signature_help_state.is_shown());
 8815    });
 8816
 8817    // Restore the popover with more parameter input
 8818    cx.set_state(indoc! {"
 8819        fn main() {
 8820            sample(param1, param2ˇ);
 8821        }
 8822
 8823        fn sample(param1: u8, param2: u8) {}
 8824    "});
 8825
 8826    let mocked_response = lsp::SignatureHelp {
 8827        signatures: vec![lsp::SignatureInformation {
 8828            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8829            documentation: None,
 8830            parameters: Some(vec![
 8831                lsp::ParameterInformation {
 8832                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8833                    documentation: None,
 8834                },
 8835                lsp::ParameterInformation {
 8836                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8837                    documentation: None,
 8838                },
 8839            ]),
 8840            active_parameter: None,
 8841        }],
 8842        active_signature: Some(0),
 8843        active_parameter: Some(1),
 8844    };
 8845    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8846    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8847        .await;
 8848
 8849    // When selecting a range, the popover is gone.
 8850    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8851    cx.update_editor(|editor, window, cx| {
 8852        editor.change_selections(None, window, cx, |s| {
 8853            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8854        })
 8855    });
 8856    cx.assert_editor_state(indoc! {"
 8857        fn main() {
 8858            sample(param1, «ˇparam2»);
 8859        }
 8860
 8861        fn sample(param1: u8, param2: u8) {}
 8862    "});
 8863    cx.editor(|editor, _, _| {
 8864        assert!(!editor.signature_help_state.is_shown());
 8865    });
 8866
 8867    // When unselecting again, the popover is back if within the brackets.
 8868    cx.update_editor(|editor, window, cx| {
 8869        editor.change_selections(None, window, cx, |s| {
 8870            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8871        })
 8872    });
 8873    cx.assert_editor_state(indoc! {"
 8874        fn main() {
 8875            sample(param1, ˇparam2);
 8876        }
 8877
 8878        fn sample(param1: u8, param2: u8) {}
 8879    "});
 8880    handle_signature_help_request(&mut cx, mocked_response).await;
 8881    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8882        .await;
 8883    cx.editor(|editor, _, _| {
 8884        assert!(editor.signature_help_state.is_shown());
 8885    });
 8886
 8887    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8888    cx.update_editor(|editor, window, cx| {
 8889        editor.change_selections(None, window, cx, |s| {
 8890            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8891            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8892        })
 8893    });
 8894    cx.assert_editor_state(indoc! {"
 8895        fn main() {
 8896            sample(param1, ˇparam2);
 8897        }
 8898
 8899        fn sample(param1: u8, param2: u8) {}
 8900    "});
 8901
 8902    let mocked_response = lsp::SignatureHelp {
 8903        signatures: vec![lsp::SignatureInformation {
 8904            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8905            documentation: None,
 8906            parameters: Some(vec![
 8907                lsp::ParameterInformation {
 8908                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8909                    documentation: None,
 8910                },
 8911                lsp::ParameterInformation {
 8912                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8913                    documentation: None,
 8914                },
 8915            ]),
 8916            active_parameter: None,
 8917        }],
 8918        active_signature: Some(0),
 8919        active_parameter: Some(1),
 8920    };
 8921    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8922    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8923        .await;
 8924    cx.update_editor(|editor, _, cx| {
 8925        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8926    });
 8927    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8928        .await;
 8929    cx.update_editor(|editor, window, cx| {
 8930        editor.change_selections(None, window, cx, |s| {
 8931            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8932        })
 8933    });
 8934    cx.assert_editor_state(indoc! {"
 8935        fn main() {
 8936            sample(param1, «ˇparam2»);
 8937        }
 8938
 8939        fn sample(param1: u8, param2: u8) {}
 8940    "});
 8941    cx.update_editor(|editor, window, cx| {
 8942        editor.change_selections(None, window, cx, |s| {
 8943            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8944        })
 8945    });
 8946    cx.assert_editor_state(indoc! {"
 8947        fn main() {
 8948            sample(param1, ˇparam2);
 8949        }
 8950
 8951        fn sample(param1: u8, param2: u8) {}
 8952    "});
 8953    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8954        .await;
 8955}
 8956
 8957#[gpui::test]
 8958async fn test_completion(cx: &mut TestAppContext) {
 8959    init_test(cx, |_| {});
 8960
 8961    let mut cx = EditorLspTestContext::new_rust(
 8962        lsp::ServerCapabilities {
 8963            completion_provider: Some(lsp::CompletionOptions {
 8964                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8965                resolve_provider: Some(true),
 8966                ..Default::default()
 8967            }),
 8968            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8969            ..Default::default()
 8970        },
 8971        cx,
 8972    )
 8973    .await;
 8974    let counter = Arc::new(AtomicUsize::new(0));
 8975
 8976    cx.set_state(indoc! {"
 8977        oneˇ
 8978        two
 8979        three
 8980    "});
 8981    cx.simulate_keystroke(".");
 8982    handle_completion_request(
 8983        &mut cx,
 8984        indoc! {"
 8985            one.|<>
 8986            two
 8987            three
 8988        "},
 8989        vec!["first_completion", "second_completion"],
 8990        counter.clone(),
 8991    )
 8992    .await;
 8993    cx.condition(|editor, _| editor.context_menu_visible())
 8994        .await;
 8995    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8996
 8997    let _handler = handle_signature_help_request(
 8998        &mut cx,
 8999        lsp::SignatureHelp {
 9000            signatures: vec![lsp::SignatureInformation {
 9001                label: "test signature".to_string(),
 9002                documentation: None,
 9003                parameters: Some(vec![lsp::ParameterInformation {
 9004                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9005                    documentation: None,
 9006                }]),
 9007                active_parameter: None,
 9008            }],
 9009            active_signature: None,
 9010            active_parameter: None,
 9011        },
 9012    );
 9013    cx.update_editor(|editor, window, cx| {
 9014        assert!(
 9015            !editor.signature_help_state.is_shown(),
 9016            "No signature help was called for"
 9017        );
 9018        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9019    });
 9020    cx.run_until_parked();
 9021    cx.update_editor(|editor, _, _| {
 9022        assert!(
 9023            !editor.signature_help_state.is_shown(),
 9024            "No signature help should be shown when completions menu is open"
 9025        );
 9026    });
 9027
 9028    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9029        editor.context_menu_next(&Default::default(), window, cx);
 9030        editor
 9031            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9032            .unwrap()
 9033    });
 9034    cx.assert_editor_state(indoc! {"
 9035        one.second_completionˇ
 9036        two
 9037        three
 9038    "});
 9039
 9040    handle_resolve_completion_request(
 9041        &mut cx,
 9042        Some(vec![
 9043            (
 9044                //This overlaps with the primary completion edit which is
 9045                //misbehavior from the LSP spec, test that we filter it out
 9046                indoc! {"
 9047                    one.second_ˇcompletion
 9048                    two
 9049                    threeˇ
 9050                "},
 9051                "overlapping additional edit",
 9052            ),
 9053            (
 9054                indoc! {"
 9055                    one.second_completion
 9056                    two
 9057                    threeˇ
 9058                "},
 9059                "\nadditional edit",
 9060            ),
 9061        ]),
 9062    )
 9063    .await;
 9064    apply_additional_edits.await.unwrap();
 9065    cx.assert_editor_state(indoc! {"
 9066        one.second_completionˇ
 9067        two
 9068        three
 9069        additional edit
 9070    "});
 9071
 9072    cx.set_state(indoc! {"
 9073        one.second_completion
 9074        twoˇ
 9075        threeˇ
 9076        additional edit
 9077    "});
 9078    cx.simulate_keystroke(" ");
 9079    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9080    cx.simulate_keystroke("s");
 9081    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9082
 9083    cx.assert_editor_state(indoc! {"
 9084        one.second_completion
 9085        two sˇ
 9086        three sˇ
 9087        additional edit
 9088    "});
 9089    handle_completion_request(
 9090        &mut cx,
 9091        indoc! {"
 9092            one.second_completion
 9093            two s
 9094            three <s|>
 9095            additional edit
 9096        "},
 9097        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9098        counter.clone(),
 9099    )
 9100    .await;
 9101    cx.condition(|editor, _| editor.context_menu_visible())
 9102        .await;
 9103    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9104
 9105    cx.simulate_keystroke("i");
 9106
 9107    handle_completion_request(
 9108        &mut cx,
 9109        indoc! {"
 9110            one.second_completion
 9111            two si
 9112            three <si|>
 9113            additional edit
 9114        "},
 9115        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9116        counter.clone(),
 9117    )
 9118    .await;
 9119    cx.condition(|editor, _| editor.context_menu_visible())
 9120        .await;
 9121    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9122
 9123    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9124        editor
 9125            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9126            .unwrap()
 9127    });
 9128    cx.assert_editor_state(indoc! {"
 9129        one.second_completion
 9130        two sixth_completionˇ
 9131        three sixth_completionˇ
 9132        additional edit
 9133    "});
 9134
 9135    apply_additional_edits.await.unwrap();
 9136
 9137    update_test_language_settings(&mut cx, |settings| {
 9138        settings.defaults.show_completions_on_input = Some(false);
 9139    });
 9140    cx.set_state("editorˇ");
 9141    cx.simulate_keystroke(".");
 9142    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9143    cx.simulate_keystroke("c");
 9144    cx.simulate_keystroke("l");
 9145    cx.simulate_keystroke("o");
 9146    cx.assert_editor_state("editor.cloˇ");
 9147    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9148    cx.update_editor(|editor, window, cx| {
 9149        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9150    });
 9151    handle_completion_request(
 9152        &mut cx,
 9153        "editor.<clo|>",
 9154        vec!["close", "clobber"],
 9155        counter.clone(),
 9156    )
 9157    .await;
 9158    cx.condition(|editor, _| editor.context_menu_visible())
 9159        .await;
 9160    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9161
 9162    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9163        editor
 9164            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9165            .unwrap()
 9166    });
 9167    cx.assert_editor_state("editor.closeˇ");
 9168    handle_resolve_completion_request(&mut cx, None).await;
 9169    apply_additional_edits.await.unwrap();
 9170}
 9171
 9172#[gpui::test]
 9173async fn test_multiline_completion(cx: &mut TestAppContext) {
 9174    init_test(cx, |_| {});
 9175
 9176    let fs = FakeFs::new(cx.executor());
 9177    fs.insert_tree(
 9178        path!("/a"),
 9179        json!({
 9180            "main.ts": "a",
 9181        }),
 9182    )
 9183    .await;
 9184
 9185    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9186    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9187    let typescript_language = Arc::new(Language::new(
 9188        LanguageConfig {
 9189            name: "TypeScript".into(),
 9190            matcher: LanguageMatcher {
 9191                path_suffixes: vec!["ts".to_string()],
 9192                ..LanguageMatcher::default()
 9193            },
 9194            line_comments: vec!["// ".into()],
 9195            ..LanguageConfig::default()
 9196        },
 9197        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9198    ));
 9199    language_registry.add(typescript_language.clone());
 9200    let mut fake_servers = language_registry.register_fake_lsp(
 9201        "TypeScript",
 9202        FakeLspAdapter {
 9203            capabilities: lsp::ServerCapabilities {
 9204                completion_provider: Some(lsp::CompletionOptions {
 9205                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9206                    ..lsp::CompletionOptions::default()
 9207                }),
 9208                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9209                ..lsp::ServerCapabilities::default()
 9210            },
 9211            // Emulate vtsls label generation
 9212            label_for_completion: Some(Box::new(|item, _| {
 9213                let text = if let Some(description) = item
 9214                    .label_details
 9215                    .as_ref()
 9216                    .and_then(|label_details| label_details.description.as_ref())
 9217                {
 9218                    format!("{} {}", item.label, description)
 9219                } else if let Some(detail) = &item.detail {
 9220                    format!("{} {}", item.label, detail)
 9221                } else {
 9222                    item.label.clone()
 9223                };
 9224                let len = text.len();
 9225                Some(language::CodeLabel {
 9226                    text,
 9227                    runs: Vec::new(),
 9228                    filter_range: 0..len,
 9229                })
 9230            })),
 9231            ..FakeLspAdapter::default()
 9232        },
 9233    );
 9234    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9235    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9236    let worktree_id = workspace
 9237        .update(cx, |workspace, _window, cx| {
 9238            workspace.project().update(cx, |project, cx| {
 9239                project.worktrees(cx).next().unwrap().read(cx).id()
 9240            })
 9241        })
 9242        .unwrap();
 9243    let _buffer = project
 9244        .update(cx, |project, cx| {
 9245            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9246        })
 9247        .await
 9248        .unwrap();
 9249    let editor = workspace
 9250        .update(cx, |workspace, window, cx| {
 9251            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9252        })
 9253        .unwrap()
 9254        .await
 9255        .unwrap()
 9256        .downcast::<Editor>()
 9257        .unwrap();
 9258    let fake_server = fake_servers.next().await.unwrap();
 9259
 9260    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9261    let multiline_label_2 = "a\nb\nc\n";
 9262    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9263    let multiline_description = "d\ne\nf\n";
 9264    let multiline_detail_2 = "g\nh\ni\n";
 9265
 9266    let mut completion_handle =
 9267        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9268            Ok(Some(lsp::CompletionResponse::Array(vec![
 9269                lsp::CompletionItem {
 9270                    label: multiline_label.to_string(),
 9271                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9272                        range: lsp::Range {
 9273                            start: lsp::Position {
 9274                                line: params.text_document_position.position.line,
 9275                                character: params.text_document_position.position.character,
 9276                            },
 9277                            end: lsp::Position {
 9278                                line: params.text_document_position.position.line,
 9279                                character: params.text_document_position.position.character,
 9280                            },
 9281                        },
 9282                        new_text: "new_text_1".to_string(),
 9283                    })),
 9284                    ..lsp::CompletionItem::default()
 9285                },
 9286                lsp::CompletionItem {
 9287                    label: "single line label 1".to_string(),
 9288                    detail: Some(multiline_detail.to_string()),
 9289                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9290                        range: lsp::Range {
 9291                            start: lsp::Position {
 9292                                line: params.text_document_position.position.line,
 9293                                character: params.text_document_position.position.character,
 9294                            },
 9295                            end: lsp::Position {
 9296                                line: params.text_document_position.position.line,
 9297                                character: params.text_document_position.position.character,
 9298                            },
 9299                        },
 9300                        new_text: "new_text_2".to_string(),
 9301                    })),
 9302                    ..lsp::CompletionItem::default()
 9303                },
 9304                lsp::CompletionItem {
 9305                    label: "single line label 2".to_string(),
 9306                    label_details: Some(lsp::CompletionItemLabelDetails {
 9307                        description: Some(multiline_description.to_string()),
 9308                        detail: None,
 9309                    }),
 9310                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9311                        range: lsp::Range {
 9312                            start: lsp::Position {
 9313                                line: params.text_document_position.position.line,
 9314                                character: params.text_document_position.position.character,
 9315                            },
 9316                            end: lsp::Position {
 9317                                line: params.text_document_position.position.line,
 9318                                character: params.text_document_position.position.character,
 9319                            },
 9320                        },
 9321                        new_text: "new_text_2".to_string(),
 9322                    })),
 9323                    ..lsp::CompletionItem::default()
 9324                },
 9325                lsp::CompletionItem {
 9326                    label: multiline_label_2.to_string(),
 9327                    detail: Some(multiline_detail_2.to_string()),
 9328                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9329                        range: lsp::Range {
 9330                            start: lsp::Position {
 9331                                line: params.text_document_position.position.line,
 9332                                character: params.text_document_position.position.character,
 9333                            },
 9334                            end: lsp::Position {
 9335                                line: params.text_document_position.position.line,
 9336                                character: params.text_document_position.position.character,
 9337                            },
 9338                        },
 9339                        new_text: "new_text_3".to_string(),
 9340                    })),
 9341                    ..lsp::CompletionItem::default()
 9342                },
 9343                lsp::CompletionItem {
 9344                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9345                    detail: Some(
 9346                        "Details with many     spaces and \t but without newlines".to_string(),
 9347                    ),
 9348                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9349                        range: lsp::Range {
 9350                            start: lsp::Position {
 9351                                line: params.text_document_position.position.line,
 9352                                character: params.text_document_position.position.character,
 9353                            },
 9354                            end: lsp::Position {
 9355                                line: params.text_document_position.position.line,
 9356                                character: params.text_document_position.position.character,
 9357                            },
 9358                        },
 9359                        new_text: "new_text_4".to_string(),
 9360                    })),
 9361                    ..lsp::CompletionItem::default()
 9362                },
 9363            ])))
 9364        });
 9365
 9366    editor.update_in(cx, |editor, window, cx| {
 9367        cx.focus_self(window);
 9368        editor.move_to_end(&MoveToEnd, window, cx);
 9369        editor.handle_input(".", window, cx);
 9370    });
 9371    cx.run_until_parked();
 9372    completion_handle.next().await.unwrap();
 9373
 9374    editor.update(cx, |editor, _| {
 9375        assert!(editor.context_menu_visible());
 9376        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9377        {
 9378            let completion_labels = menu
 9379                .completions
 9380                .borrow()
 9381                .iter()
 9382                .map(|c| c.label.text.clone())
 9383                .collect::<Vec<_>>();
 9384            assert_eq!(
 9385                completion_labels,
 9386                &[
 9387                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9388                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9389                    "single line label 2 d e f ",
 9390                    "a b c g h i ",
 9391                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9392                ],
 9393                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9394            );
 9395
 9396            for completion in menu
 9397                .completions
 9398                .borrow()
 9399                .iter() {
 9400                    assert_eq!(
 9401                        completion.label.filter_range,
 9402                        0..completion.label.text.len(),
 9403                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9404                    );
 9405                }
 9406
 9407        } else {
 9408            panic!("expected completion menu to be open");
 9409        }
 9410    });
 9411}
 9412
 9413#[gpui::test]
 9414async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9415    init_test(cx, |_| {});
 9416    let mut cx = EditorLspTestContext::new_rust(
 9417        lsp::ServerCapabilities {
 9418            completion_provider: Some(lsp::CompletionOptions {
 9419                trigger_characters: Some(vec![".".to_string()]),
 9420                ..Default::default()
 9421            }),
 9422            ..Default::default()
 9423        },
 9424        cx,
 9425    )
 9426    .await;
 9427    cx.lsp
 9428        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9429            Ok(Some(lsp::CompletionResponse::Array(vec![
 9430                lsp::CompletionItem {
 9431                    label: "first".into(),
 9432                    ..Default::default()
 9433                },
 9434                lsp::CompletionItem {
 9435                    label: "last".into(),
 9436                    ..Default::default()
 9437                },
 9438            ])))
 9439        });
 9440    cx.set_state("variableˇ");
 9441    cx.simulate_keystroke(".");
 9442    cx.executor().run_until_parked();
 9443
 9444    cx.update_editor(|editor, _, _| {
 9445        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9446        {
 9447            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9448        } else {
 9449            panic!("expected completion menu to be open");
 9450        }
 9451    });
 9452
 9453    cx.update_editor(|editor, window, cx| {
 9454        editor.move_page_down(&MovePageDown::default(), window, cx);
 9455        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9456        {
 9457            assert!(
 9458                menu.selected_item == 1,
 9459                "expected PageDown to select the last item from the context menu"
 9460            );
 9461        } else {
 9462            panic!("expected completion menu to stay open after PageDown");
 9463        }
 9464    });
 9465
 9466    cx.update_editor(|editor, window, cx| {
 9467        editor.move_page_up(&MovePageUp::default(), window, cx);
 9468        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9469        {
 9470            assert!(
 9471                menu.selected_item == 0,
 9472                "expected PageUp to select the first item from the context menu"
 9473            );
 9474        } else {
 9475            panic!("expected completion menu to stay open after PageUp");
 9476        }
 9477    });
 9478}
 9479
 9480#[gpui::test]
 9481async fn test_completion_sort(cx: &mut TestAppContext) {
 9482    init_test(cx, |_| {});
 9483    let mut cx = EditorLspTestContext::new_rust(
 9484        lsp::ServerCapabilities {
 9485            completion_provider: Some(lsp::CompletionOptions {
 9486                trigger_characters: Some(vec![".".to_string()]),
 9487                ..Default::default()
 9488            }),
 9489            ..Default::default()
 9490        },
 9491        cx,
 9492    )
 9493    .await;
 9494    cx.lsp
 9495        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9496            Ok(Some(lsp::CompletionResponse::Array(vec![
 9497                lsp::CompletionItem {
 9498                    label: "Range".into(),
 9499                    sort_text: Some("a".into()),
 9500                    ..Default::default()
 9501                },
 9502                lsp::CompletionItem {
 9503                    label: "r".into(),
 9504                    sort_text: Some("b".into()),
 9505                    ..Default::default()
 9506                },
 9507                lsp::CompletionItem {
 9508                    label: "ret".into(),
 9509                    sort_text: Some("c".into()),
 9510                    ..Default::default()
 9511                },
 9512                lsp::CompletionItem {
 9513                    label: "return".into(),
 9514                    sort_text: Some("d".into()),
 9515                    ..Default::default()
 9516                },
 9517                lsp::CompletionItem {
 9518                    label: "slice".into(),
 9519                    sort_text: Some("d".into()),
 9520                    ..Default::default()
 9521                },
 9522            ])))
 9523        });
 9524    cx.set_state("");
 9525    cx.executor().run_until_parked();
 9526    cx.update_editor(|editor, window, cx| {
 9527        editor.show_completions(
 9528            &ShowCompletions {
 9529                trigger: Some("r".into()),
 9530            },
 9531            window,
 9532            cx,
 9533        );
 9534    });
 9535    cx.executor().run_until_parked();
 9536
 9537    cx.update_editor(|editor, _, _| {
 9538        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9539        {
 9540            assert_eq!(
 9541                completion_menu_entries(&menu),
 9542                &["r", "ret", "Range", "return"]
 9543            );
 9544        } else {
 9545            panic!("expected completion menu to be open");
 9546        }
 9547    });
 9548}
 9549
 9550#[gpui::test]
 9551async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9552    init_test(cx, |_| {});
 9553
 9554    let mut cx = EditorLspTestContext::new_rust(
 9555        lsp::ServerCapabilities {
 9556            completion_provider: Some(lsp::CompletionOptions {
 9557                trigger_characters: Some(vec![".".to_string()]),
 9558                resolve_provider: Some(true),
 9559                ..Default::default()
 9560            }),
 9561            ..Default::default()
 9562        },
 9563        cx,
 9564    )
 9565    .await;
 9566
 9567    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9568    cx.simulate_keystroke(".");
 9569    let completion_item = lsp::CompletionItem {
 9570        label: "Some".into(),
 9571        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9572        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9573        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9574            kind: lsp::MarkupKind::Markdown,
 9575            value: "```rust\nSome(2)\n```".to_string(),
 9576        })),
 9577        deprecated: Some(false),
 9578        sort_text: Some("Some".to_string()),
 9579        filter_text: Some("Some".to_string()),
 9580        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9581        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9582            range: lsp::Range {
 9583                start: lsp::Position {
 9584                    line: 0,
 9585                    character: 22,
 9586                },
 9587                end: lsp::Position {
 9588                    line: 0,
 9589                    character: 22,
 9590                },
 9591            },
 9592            new_text: "Some(2)".to_string(),
 9593        })),
 9594        additional_text_edits: Some(vec![lsp::TextEdit {
 9595            range: lsp::Range {
 9596                start: lsp::Position {
 9597                    line: 0,
 9598                    character: 20,
 9599                },
 9600                end: lsp::Position {
 9601                    line: 0,
 9602                    character: 22,
 9603                },
 9604            },
 9605            new_text: "".to_string(),
 9606        }]),
 9607        ..Default::default()
 9608    };
 9609
 9610    let closure_completion_item = completion_item.clone();
 9611    let counter = Arc::new(AtomicUsize::new(0));
 9612    let counter_clone = counter.clone();
 9613    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9614        let task_completion_item = closure_completion_item.clone();
 9615        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9616        async move {
 9617            Ok(Some(lsp::CompletionResponse::Array(vec![
 9618                task_completion_item,
 9619            ])))
 9620        }
 9621    });
 9622
 9623    cx.condition(|editor, _| editor.context_menu_visible())
 9624        .await;
 9625    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9626    assert!(request.next().await.is_some());
 9627    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9628
 9629    cx.simulate_keystroke("S");
 9630    cx.simulate_keystroke("o");
 9631    cx.simulate_keystroke("m");
 9632    cx.condition(|editor, _| editor.context_menu_visible())
 9633        .await;
 9634    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9635    assert!(request.next().await.is_some());
 9636    assert!(request.next().await.is_some());
 9637    assert!(request.next().await.is_some());
 9638    request.close();
 9639    assert!(request.next().await.is_none());
 9640    assert_eq!(
 9641        counter.load(atomic::Ordering::Acquire),
 9642        4,
 9643        "With the completions menu open, only one LSP request should happen per input"
 9644    );
 9645}
 9646
 9647#[gpui::test]
 9648async fn test_toggle_comment(cx: &mut TestAppContext) {
 9649    init_test(cx, |_| {});
 9650    let mut cx = EditorTestContext::new(cx).await;
 9651    let language = Arc::new(Language::new(
 9652        LanguageConfig {
 9653            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9654            ..Default::default()
 9655        },
 9656        Some(tree_sitter_rust::LANGUAGE.into()),
 9657    ));
 9658    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9659
 9660    // If multiple selections intersect a line, the line is only toggled once.
 9661    cx.set_state(indoc! {"
 9662        fn a() {
 9663            «//b();
 9664            ˇ»// «c();
 9665            //ˇ»  d();
 9666        }
 9667    "});
 9668
 9669    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9670
 9671    cx.assert_editor_state(indoc! {"
 9672        fn a() {
 9673            «b();
 9674            c();
 9675            ˇ» d();
 9676        }
 9677    "});
 9678
 9679    // The comment prefix is inserted at the same column for every line in a
 9680    // selection.
 9681    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9682
 9683    cx.assert_editor_state(indoc! {"
 9684        fn a() {
 9685            // «b();
 9686            // c();
 9687            ˇ»//  d();
 9688        }
 9689    "});
 9690
 9691    // If a selection ends at the beginning of a line, that line is not toggled.
 9692    cx.set_selections_state(indoc! {"
 9693        fn a() {
 9694            // b();
 9695            «// c();
 9696        ˇ»    //  d();
 9697        }
 9698    "});
 9699
 9700    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9701
 9702    cx.assert_editor_state(indoc! {"
 9703        fn a() {
 9704            // b();
 9705            «c();
 9706        ˇ»    //  d();
 9707        }
 9708    "});
 9709
 9710    // If a selection span a single line and is empty, the line is toggled.
 9711    cx.set_state(indoc! {"
 9712        fn a() {
 9713            a();
 9714            b();
 9715        ˇ
 9716        }
 9717    "});
 9718
 9719    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9720
 9721    cx.assert_editor_state(indoc! {"
 9722        fn a() {
 9723            a();
 9724            b();
 9725        //•ˇ
 9726        }
 9727    "});
 9728
 9729    // If a selection span multiple lines, empty lines are not toggled.
 9730    cx.set_state(indoc! {"
 9731        fn a() {
 9732            «a();
 9733
 9734            c();ˇ»
 9735        }
 9736    "});
 9737
 9738    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9739
 9740    cx.assert_editor_state(indoc! {"
 9741        fn a() {
 9742            // «a();
 9743
 9744            // c();ˇ»
 9745        }
 9746    "});
 9747
 9748    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9749    cx.set_state(indoc! {"
 9750        fn a() {
 9751            «// a();
 9752            /// b();
 9753            //! c();ˇ»
 9754        }
 9755    "});
 9756
 9757    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9758
 9759    cx.assert_editor_state(indoc! {"
 9760        fn a() {
 9761            «a();
 9762            b();
 9763            c();ˇ»
 9764        }
 9765    "});
 9766}
 9767
 9768#[gpui::test]
 9769async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9770    init_test(cx, |_| {});
 9771    let mut cx = EditorTestContext::new(cx).await;
 9772    let language = Arc::new(Language::new(
 9773        LanguageConfig {
 9774            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9775            ..Default::default()
 9776        },
 9777        Some(tree_sitter_rust::LANGUAGE.into()),
 9778    ));
 9779    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9780
 9781    let toggle_comments = &ToggleComments {
 9782        advance_downwards: false,
 9783        ignore_indent: true,
 9784    };
 9785
 9786    // If multiple selections intersect a line, the line is only toggled once.
 9787    cx.set_state(indoc! {"
 9788        fn a() {
 9789        //    «b();
 9790        //    c();
 9791        //    ˇ» d();
 9792        }
 9793    "});
 9794
 9795    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9796
 9797    cx.assert_editor_state(indoc! {"
 9798        fn a() {
 9799            «b();
 9800            c();
 9801            ˇ» d();
 9802        }
 9803    "});
 9804
 9805    // The comment prefix is inserted at the beginning of each line
 9806    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9807
 9808    cx.assert_editor_state(indoc! {"
 9809        fn a() {
 9810        //    «b();
 9811        //    c();
 9812        //    ˇ» d();
 9813        }
 9814    "});
 9815
 9816    // If a selection ends at the beginning of a line, that line is not toggled.
 9817    cx.set_selections_state(indoc! {"
 9818        fn a() {
 9819        //    b();
 9820        //    «c();
 9821        ˇ»//     d();
 9822        }
 9823    "});
 9824
 9825    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9826
 9827    cx.assert_editor_state(indoc! {"
 9828        fn a() {
 9829        //    b();
 9830            «c();
 9831        ˇ»//     d();
 9832        }
 9833    "});
 9834
 9835    // If a selection span a single line and is empty, the line is toggled.
 9836    cx.set_state(indoc! {"
 9837        fn a() {
 9838            a();
 9839            b();
 9840        ˇ
 9841        }
 9842    "});
 9843
 9844    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9845
 9846    cx.assert_editor_state(indoc! {"
 9847        fn a() {
 9848            a();
 9849            b();
 9850        //ˇ
 9851        }
 9852    "});
 9853
 9854    // If a selection span multiple lines, empty lines are not toggled.
 9855    cx.set_state(indoc! {"
 9856        fn a() {
 9857            «a();
 9858
 9859            c();ˇ»
 9860        }
 9861    "});
 9862
 9863    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9864
 9865    cx.assert_editor_state(indoc! {"
 9866        fn a() {
 9867        //    «a();
 9868
 9869        //    c();ˇ»
 9870        }
 9871    "});
 9872
 9873    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9874    cx.set_state(indoc! {"
 9875        fn a() {
 9876        //    «a();
 9877        ///    b();
 9878        //!    c();ˇ»
 9879        }
 9880    "});
 9881
 9882    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9883
 9884    cx.assert_editor_state(indoc! {"
 9885        fn a() {
 9886            «a();
 9887            b();
 9888            c();ˇ»
 9889        }
 9890    "});
 9891}
 9892
 9893#[gpui::test]
 9894async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9895    init_test(cx, |_| {});
 9896
 9897    let language = Arc::new(Language::new(
 9898        LanguageConfig {
 9899            line_comments: vec!["// ".into()],
 9900            ..Default::default()
 9901        },
 9902        Some(tree_sitter_rust::LANGUAGE.into()),
 9903    ));
 9904
 9905    let mut cx = EditorTestContext::new(cx).await;
 9906
 9907    cx.language_registry().add(language.clone());
 9908    cx.update_buffer(|buffer, cx| {
 9909        buffer.set_language(Some(language), cx);
 9910    });
 9911
 9912    let toggle_comments = &ToggleComments {
 9913        advance_downwards: true,
 9914        ignore_indent: false,
 9915    };
 9916
 9917    // Single cursor on one line -> advance
 9918    // Cursor moves horizontally 3 characters as well on non-blank line
 9919    cx.set_state(indoc!(
 9920        "fn a() {
 9921             ˇdog();
 9922             cat();
 9923        }"
 9924    ));
 9925    cx.update_editor(|editor, window, cx| {
 9926        editor.toggle_comments(toggle_comments, window, cx);
 9927    });
 9928    cx.assert_editor_state(indoc!(
 9929        "fn a() {
 9930             // dog();
 9931             catˇ();
 9932        }"
 9933    ));
 9934
 9935    // Single selection on one line -> don't advance
 9936    cx.set_state(indoc!(
 9937        "fn a() {
 9938             «dog()ˇ»;
 9939             cat();
 9940        }"
 9941    ));
 9942    cx.update_editor(|editor, window, cx| {
 9943        editor.toggle_comments(toggle_comments, window, cx);
 9944    });
 9945    cx.assert_editor_state(indoc!(
 9946        "fn a() {
 9947             // «dog()ˇ»;
 9948             cat();
 9949        }"
 9950    ));
 9951
 9952    // Multiple cursors on one line -> advance
 9953    cx.set_state(indoc!(
 9954        "fn a() {
 9955             ˇdˇog();
 9956             cat();
 9957        }"
 9958    ));
 9959    cx.update_editor(|editor, window, cx| {
 9960        editor.toggle_comments(toggle_comments, window, cx);
 9961    });
 9962    cx.assert_editor_state(indoc!(
 9963        "fn a() {
 9964             // dog();
 9965             catˇ(ˇ);
 9966        }"
 9967    ));
 9968
 9969    // Multiple cursors on one line, with selection -> don't advance
 9970    cx.set_state(indoc!(
 9971        "fn a() {
 9972             ˇdˇog«()ˇ»;
 9973             cat();
 9974        }"
 9975    ));
 9976    cx.update_editor(|editor, window, cx| {
 9977        editor.toggle_comments(toggle_comments, window, cx);
 9978    });
 9979    cx.assert_editor_state(indoc!(
 9980        "fn a() {
 9981             // ˇdˇog«()ˇ»;
 9982             cat();
 9983        }"
 9984    ));
 9985
 9986    // Single cursor on one line -> advance
 9987    // Cursor moves to column 0 on blank line
 9988    cx.set_state(indoc!(
 9989        "fn a() {
 9990             ˇdog();
 9991
 9992             cat();
 9993        }"
 9994    ));
 9995    cx.update_editor(|editor, window, cx| {
 9996        editor.toggle_comments(toggle_comments, window, cx);
 9997    });
 9998    cx.assert_editor_state(indoc!(
 9999        "fn a() {
10000             // dog();
10001        ˇ
10002             cat();
10003        }"
10004    ));
10005
10006    // Single cursor on one line -> advance
10007    // Cursor starts and ends at column 0
10008    cx.set_state(indoc!(
10009        "fn a() {
10010         ˇ    dog();
10011             cat();
10012        }"
10013    ));
10014    cx.update_editor(|editor, window, cx| {
10015        editor.toggle_comments(toggle_comments, window, cx);
10016    });
10017    cx.assert_editor_state(indoc!(
10018        "fn a() {
10019             // dog();
10020         ˇ    cat();
10021        }"
10022    ));
10023}
10024
10025#[gpui::test]
10026async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10027    init_test(cx, |_| {});
10028
10029    let mut cx = EditorTestContext::new(cx).await;
10030
10031    let html_language = Arc::new(
10032        Language::new(
10033            LanguageConfig {
10034                name: "HTML".into(),
10035                block_comment: Some(("<!-- ".into(), " -->".into())),
10036                ..Default::default()
10037            },
10038            Some(tree_sitter_html::LANGUAGE.into()),
10039        )
10040        .with_injection_query(
10041            r#"
10042            (script_element
10043                (raw_text) @injection.content
10044                (#set! injection.language "javascript"))
10045            "#,
10046        )
10047        .unwrap(),
10048    );
10049
10050    let javascript_language = Arc::new(Language::new(
10051        LanguageConfig {
10052            name: "JavaScript".into(),
10053            line_comments: vec!["// ".into()],
10054            ..Default::default()
10055        },
10056        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10057    ));
10058
10059    cx.language_registry().add(html_language.clone());
10060    cx.language_registry().add(javascript_language.clone());
10061    cx.update_buffer(|buffer, cx| {
10062        buffer.set_language(Some(html_language), cx);
10063    });
10064
10065    // Toggle comments for empty selections
10066    cx.set_state(
10067        &r#"
10068            <p>A</p>ˇ
10069            <p>B</p>ˇ
10070            <p>C</p>ˇ
10071        "#
10072        .unindent(),
10073    );
10074    cx.update_editor(|editor, window, cx| {
10075        editor.toggle_comments(&ToggleComments::default(), window, cx)
10076    });
10077    cx.assert_editor_state(
10078        &r#"
10079            <!-- <p>A</p>ˇ -->
10080            <!-- <p>B</p>ˇ -->
10081            <!-- <p>C</p>ˇ -->
10082        "#
10083        .unindent(),
10084    );
10085    cx.update_editor(|editor, window, cx| {
10086        editor.toggle_comments(&ToggleComments::default(), window, cx)
10087    });
10088    cx.assert_editor_state(
10089        &r#"
10090            <p>A</p>ˇ
10091            <p>B</p>ˇ
10092            <p>C</p>ˇ
10093        "#
10094        .unindent(),
10095    );
10096
10097    // Toggle comments for mixture of empty and non-empty selections, where
10098    // multiple selections occupy a given line.
10099    cx.set_state(
10100        &r#"
10101            <p>A«</p>
10102            <p>ˇ»B</p>ˇ
10103            <p>C«</p>
10104            <p>ˇ»D</p>ˇ
10105        "#
10106        .unindent(),
10107    );
10108
10109    cx.update_editor(|editor, window, cx| {
10110        editor.toggle_comments(&ToggleComments::default(), window, cx)
10111    });
10112    cx.assert_editor_state(
10113        &r#"
10114            <!-- <p>A«</p>
10115            <p>ˇ»B</p>ˇ -->
10116            <!-- <p>C«</p>
10117            <p>ˇ»D</p>ˇ -->
10118        "#
10119        .unindent(),
10120    );
10121    cx.update_editor(|editor, window, cx| {
10122        editor.toggle_comments(&ToggleComments::default(), window, cx)
10123    });
10124    cx.assert_editor_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    // Toggle comments when different languages are active for different
10135    // selections.
10136    cx.set_state(
10137        &r#"
10138            ˇ<script>
10139                ˇvar x = new Y();
10140            ˇ</script>
10141        "#
10142        .unindent(),
10143    );
10144    cx.executor().run_until_parked();
10145    cx.update_editor(|editor, window, cx| {
10146        editor.toggle_comments(&ToggleComments::default(), window, cx)
10147    });
10148    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10149    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10150    cx.assert_editor_state(
10151        &r#"
10152            <!-- ˇ<script> -->
10153                // ˇvar x = new Y();
10154            <!-- ˇ</script> -->
10155        "#
10156        .unindent(),
10157    );
10158}
10159
10160#[gpui::test]
10161fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10162    init_test(cx, |_| {});
10163
10164    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10165    let multibuffer = cx.new(|cx| {
10166        let mut multibuffer = MultiBuffer::new(ReadWrite);
10167        multibuffer.push_excerpts(
10168            buffer.clone(),
10169            [
10170                ExcerptRange {
10171                    context: Point::new(0, 0)..Point::new(0, 4),
10172                    primary: None,
10173                },
10174                ExcerptRange {
10175                    context: Point::new(1, 0)..Point::new(1, 4),
10176                    primary: None,
10177                },
10178            ],
10179            cx,
10180        );
10181        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10182        multibuffer
10183    });
10184
10185    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10186    editor.update_in(cx, |editor, window, cx| {
10187        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10188        editor.change_selections(None, window, cx, |s| {
10189            s.select_ranges([
10190                Point::new(0, 0)..Point::new(0, 0),
10191                Point::new(1, 0)..Point::new(1, 0),
10192            ])
10193        });
10194
10195        editor.handle_input("X", window, cx);
10196        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10197        assert_eq!(
10198            editor.selections.ranges(cx),
10199            [
10200                Point::new(0, 1)..Point::new(0, 1),
10201                Point::new(1, 1)..Point::new(1, 1),
10202            ]
10203        );
10204
10205        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10206        editor.change_selections(None, window, cx, |s| {
10207            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10208        });
10209        editor.backspace(&Default::default(), window, cx);
10210        assert_eq!(editor.text(cx), "Xa\nbbb");
10211        assert_eq!(
10212            editor.selections.ranges(cx),
10213            [Point::new(1, 0)..Point::new(1, 0)]
10214        );
10215
10216        editor.change_selections(None, window, cx, |s| {
10217            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10218        });
10219        editor.backspace(&Default::default(), window, cx);
10220        assert_eq!(editor.text(cx), "X\nbb");
10221        assert_eq!(
10222            editor.selections.ranges(cx),
10223            [Point::new(0, 1)..Point::new(0, 1)]
10224        );
10225    });
10226}
10227
10228#[gpui::test]
10229fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10230    init_test(cx, |_| {});
10231
10232    let markers = vec![('[', ']').into(), ('(', ')').into()];
10233    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10234        indoc! {"
10235            [aaaa
10236            (bbbb]
10237            cccc)",
10238        },
10239        markers.clone(),
10240    );
10241    let excerpt_ranges = markers.into_iter().map(|marker| {
10242        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10243        ExcerptRange {
10244            context,
10245            primary: None,
10246        }
10247    });
10248    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10249    let multibuffer = cx.new(|cx| {
10250        let mut multibuffer = MultiBuffer::new(ReadWrite);
10251        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10252        multibuffer
10253    });
10254
10255    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10256    editor.update_in(cx, |editor, window, cx| {
10257        let (expected_text, selection_ranges) = marked_text_ranges(
10258            indoc! {"
10259                aaaa
10260                bˇbbb
10261                bˇbbˇb
10262                cccc"
10263            },
10264            true,
10265        );
10266        assert_eq!(editor.text(cx), expected_text);
10267        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10268
10269        editor.handle_input("X", window, cx);
10270
10271        let (expected_text, expected_selections) = marked_text_ranges(
10272            indoc! {"
10273                aaaa
10274                bXˇbbXb
10275                bXˇbbXˇb
10276                cccc"
10277            },
10278            false,
10279        );
10280        assert_eq!(editor.text(cx), expected_text);
10281        assert_eq!(editor.selections.ranges(cx), expected_selections);
10282
10283        editor.newline(&Newline, window, cx);
10284        let (expected_text, expected_selections) = marked_text_ranges(
10285            indoc! {"
10286                aaaa
10287                bX
10288                ˇbbX
10289                b
10290                bX
10291                ˇbbX
10292                ˇb
10293                cccc"
10294            },
10295            false,
10296        );
10297        assert_eq!(editor.text(cx), expected_text);
10298        assert_eq!(editor.selections.ranges(cx), expected_selections);
10299    });
10300}
10301
10302#[gpui::test]
10303fn test_refresh_selections(cx: &mut TestAppContext) {
10304    init_test(cx, |_| {});
10305
10306    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10307    let mut excerpt1_id = None;
10308    let multibuffer = cx.new(|cx| {
10309        let mut multibuffer = MultiBuffer::new(ReadWrite);
10310        excerpt1_id = multibuffer
10311            .push_excerpts(
10312                buffer.clone(),
10313                [
10314                    ExcerptRange {
10315                        context: Point::new(0, 0)..Point::new(1, 4),
10316                        primary: None,
10317                    },
10318                    ExcerptRange {
10319                        context: Point::new(1, 0)..Point::new(2, 4),
10320                        primary: None,
10321                    },
10322                ],
10323                cx,
10324            )
10325            .into_iter()
10326            .next();
10327        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10328        multibuffer
10329    });
10330
10331    let editor = cx.add_window(|window, cx| {
10332        let mut editor = build_editor(multibuffer.clone(), window, cx);
10333        let snapshot = editor.snapshot(window, cx);
10334        editor.change_selections(None, window, cx, |s| {
10335            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10336        });
10337        editor.begin_selection(
10338            Point::new(2, 1).to_display_point(&snapshot),
10339            true,
10340            1,
10341            window,
10342            cx,
10343        );
10344        assert_eq!(
10345            editor.selections.ranges(cx),
10346            [
10347                Point::new(1, 3)..Point::new(1, 3),
10348                Point::new(2, 1)..Point::new(2, 1),
10349            ]
10350        );
10351        editor
10352    });
10353
10354    // Refreshing selections is a no-op when excerpts haven't changed.
10355    _ = editor.update(cx, |editor, window, cx| {
10356        editor.change_selections(None, window, cx, |s| s.refresh());
10357        assert_eq!(
10358            editor.selections.ranges(cx),
10359            [
10360                Point::new(1, 3)..Point::new(1, 3),
10361                Point::new(2, 1)..Point::new(2, 1),
10362            ]
10363        );
10364    });
10365
10366    multibuffer.update(cx, |multibuffer, cx| {
10367        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10368    });
10369    _ = editor.update(cx, |editor, window, cx| {
10370        // Removing an excerpt causes the first selection to become degenerate.
10371        assert_eq!(
10372            editor.selections.ranges(cx),
10373            [
10374                Point::new(0, 0)..Point::new(0, 0),
10375                Point::new(0, 1)..Point::new(0, 1)
10376            ]
10377        );
10378
10379        // Refreshing selections will relocate the first selection to the original buffer
10380        // location.
10381        editor.change_selections(None, window, cx, |s| s.refresh());
10382        assert_eq!(
10383            editor.selections.ranges(cx),
10384            [
10385                Point::new(0, 1)..Point::new(0, 1),
10386                Point::new(0, 3)..Point::new(0, 3)
10387            ]
10388        );
10389        assert!(editor.selections.pending_anchor().is_some());
10390    });
10391}
10392
10393#[gpui::test]
10394fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10395    init_test(cx, |_| {});
10396
10397    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10398    let mut excerpt1_id = None;
10399    let multibuffer = cx.new(|cx| {
10400        let mut multibuffer = MultiBuffer::new(ReadWrite);
10401        excerpt1_id = multibuffer
10402            .push_excerpts(
10403                buffer.clone(),
10404                [
10405                    ExcerptRange {
10406                        context: Point::new(0, 0)..Point::new(1, 4),
10407                        primary: None,
10408                    },
10409                    ExcerptRange {
10410                        context: Point::new(1, 0)..Point::new(2, 4),
10411                        primary: None,
10412                    },
10413                ],
10414                cx,
10415            )
10416            .into_iter()
10417            .next();
10418        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10419        multibuffer
10420    });
10421
10422    let editor = cx.add_window(|window, cx| {
10423        let mut editor = build_editor(multibuffer.clone(), window, cx);
10424        let snapshot = editor.snapshot(window, cx);
10425        editor.begin_selection(
10426            Point::new(1, 3).to_display_point(&snapshot),
10427            false,
10428            1,
10429            window,
10430            cx,
10431        );
10432        assert_eq!(
10433            editor.selections.ranges(cx),
10434            [Point::new(1, 3)..Point::new(1, 3)]
10435        );
10436        editor
10437    });
10438
10439    multibuffer.update(cx, |multibuffer, cx| {
10440        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10441    });
10442    _ = editor.update(cx, |editor, window, cx| {
10443        assert_eq!(
10444            editor.selections.ranges(cx),
10445            [Point::new(0, 0)..Point::new(0, 0)]
10446        );
10447
10448        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10449        editor.change_selections(None, window, cx, |s| s.refresh());
10450        assert_eq!(
10451            editor.selections.ranges(cx),
10452            [Point::new(0, 3)..Point::new(0, 3)]
10453        );
10454        assert!(editor.selections.pending_anchor().is_some());
10455    });
10456}
10457
10458#[gpui::test]
10459async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10460    init_test(cx, |_| {});
10461
10462    let language = Arc::new(
10463        Language::new(
10464            LanguageConfig {
10465                brackets: BracketPairConfig {
10466                    pairs: vec![
10467                        BracketPair {
10468                            start: "{".to_string(),
10469                            end: "}".to_string(),
10470                            close: true,
10471                            surround: true,
10472                            newline: true,
10473                        },
10474                        BracketPair {
10475                            start: "/* ".to_string(),
10476                            end: " */".to_string(),
10477                            close: true,
10478                            surround: true,
10479                            newline: true,
10480                        },
10481                    ],
10482                    ..Default::default()
10483                },
10484                ..Default::default()
10485            },
10486            Some(tree_sitter_rust::LANGUAGE.into()),
10487        )
10488        .with_indents_query("")
10489        .unwrap(),
10490    );
10491
10492    let text = concat!(
10493        "{   }\n",     //
10494        "  x\n",       //
10495        "  /*   */\n", //
10496        "x\n",         //
10497        "{{} }\n",     //
10498    );
10499
10500    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10501    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10502    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10503    editor
10504        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10505        .await;
10506
10507    editor.update_in(cx, |editor, window, cx| {
10508        editor.change_selections(None, window, cx, |s| {
10509            s.select_display_ranges([
10510                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10511                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10512                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10513            ])
10514        });
10515        editor.newline(&Newline, window, cx);
10516
10517        assert_eq!(
10518            editor.buffer().read(cx).read(cx).text(),
10519            concat!(
10520                "{ \n",    // Suppress rustfmt
10521                "\n",      //
10522                "}\n",     //
10523                "  x\n",   //
10524                "  /* \n", //
10525                "  \n",    //
10526                "  */\n",  //
10527                "x\n",     //
10528                "{{} \n",  //
10529                "}\n",     //
10530            )
10531        );
10532    });
10533}
10534
10535#[gpui::test]
10536fn test_highlighted_ranges(cx: &mut TestAppContext) {
10537    init_test(cx, |_| {});
10538
10539    let editor = cx.add_window(|window, cx| {
10540        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10541        build_editor(buffer.clone(), window, cx)
10542    });
10543
10544    _ = editor.update(cx, |editor, window, cx| {
10545        struct Type1;
10546        struct Type2;
10547
10548        let buffer = editor.buffer.read(cx).snapshot(cx);
10549
10550        let anchor_range =
10551            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10552
10553        editor.highlight_background::<Type1>(
10554            &[
10555                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10556                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10557                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10558                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10559            ],
10560            |_| Hsla::red(),
10561            cx,
10562        );
10563        editor.highlight_background::<Type2>(
10564            &[
10565                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10566                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10567                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10568                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10569            ],
10570            |_| Hsla::green(),
10571            cx,
10572        );
10573
10574        let snapshot = editor.snapshot(window, cx);
10575        let mut highlighted_ranges = editor.background_highlights_in_range(
10576            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10577            &snapshot,
10578            cx.theme().colors(),
10579        );
10580        // Enforce a consistent ordering based on color without relying on the ordering of the
10581        // highlight's `TypeId` which is non-executor.
10582        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10583        assert_eq!(
10584            highlighted_ranges,
10585            &[
10586                (
10587                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10588                    Hsla::red(),
10589                ),
10590                (
10591                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10592                    Hsla::red(),
10593                ),
10594                (
10595                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10596                    Hsla::green(),
10597                ),
10598                (
10599                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10600                    Hsla::green(),
10601                ),
10602            ]
10603        );
10604        assert_eq!(
10605            editor.background_highlights_in_range(
10606                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10607                &snapshot,
10608                cx.theme().colors(),
10609            ),
10610            &[(
10611                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10612                Hsla::red(),
10613            )]
10614        );
10615    });
10616}
10617
10618#[gpui::test]
10619async fn test_following(cx: &mut TestAppContext) {
10620    init_test(cx, |_| {});
10621
10622    let fs = FakeFs::new(cx.executor());
10623    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10624
10625    let buffer = project.update(cx, |project, cx| {
10626        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10627        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10628    });
10629    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10630    let follower = cx.update(|cx| {
10631        cx.open_window(
10632            WindowOptions {
10633                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10634                    gpui::Point::new(px(0.), px(0.)),
10635                    gpui::Point::new(px(10.), px(80.)),
10636                ))),
10637                ..Default::default()
10638            },
10639            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10640        )
10641        .unwrap()
10642    });
10643
10644    let is_still_following = Rc::new(RefCell::new(true));
10645    let follower_edit_event_count = Rc::new(RefCell::new(0));
10646    let pending_update = Rc::new(RefCell::new(None));
10647    let leader_entity = leader.root(cx).unwrap();
10648    let follower_entity = follower.root(cx).unwrap();
10649    _ = follower.update(cx, {
10650        let update = pending_update.clone();
10651        let is_still_following = is_still_following.clone();
10652        let follower_edit_event_count = follower_edit_event_count.clone();
10653        |_, window, cx| {
10654            cx.subscribe_in(
10655                &leader_entity,
10656                window,
10657                move |_, leader, event, window, cx| {
10658                    leader.read(cx).add_event_to_update_proto(
10659                        event,
10660                        &mut update.borrow_mut(),
10661                        window,
10662                        cx,
10663                    );
10664                },
10665            )
10666            .detach();
10667
10668            cx.subscribe_in(
10669                &follower_entity,
10670                window,
10671                move |_, _, event: &EditorEvent, _window, _cx| {
10672                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10673                        *is_still_following.borrow_mut() = false;
10674                    }
10675
10676                    if let EditorEvent::BufferEdited = event {
10677                        *follower_edit_event_count.borrow_mut() += 1;
10678                    }
10679                },
10680            )
10681            .detach();
10682        }
10683    });
10684
10685    // Update the selections only
10686    _ = leader.update(cx, |leader, window, cx| {
10687        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10688    });
10689    follower
10690        .update(cx, |follower, window, cx| {
10691            follower.apply_update_proto(
10692                &project,
10693                pending_update.borrow_mut().take().unwrap(),
10694                window,
10695                cx,
10696            )
10697        })
10698        .unwrap()
10699        .await
10700        .unwrap();
10701    _ = follower.update(cx, |follower, _, cx| {
10702        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10703    });
10704    assert!(*is_still_following.borrow());
10705    assert_eq!(*follower_edit_event_count.borrow(), 0);
10706
10707    // Update the scroll position only
10708    _ = leader.update(cx, |leader, window, cx| {
10709        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10710    });
10711    follower
10712        .update(cx, |follower, window, cx| {
10713            follower.apply_update_proto(
10714                &project,
10715                pending_update.borrow_mut().take().unwrap(),
10716                window,
10717                cx,
10718            )
10719        })
10720        .unwrap()
10721        .await
10722        .unwrap();
10723    assert_eq!(
10724        follower
10725            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10726            .unwrap(),
10727        gpui::Point::new(1.5, 3.5)
10728    );
10729    assert!(*is_still_following.borrow());
10730    assert_eq!(*follower_edit_event_count.borrow(), 0);
10731
10732    // Update the selections and scroll position. The follower's scroll position is updated
10733    // via autoscroll, not via the leader's exact scroll position.
10734    _ = leader.update(cx, |leader, window, cx| {
10735        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10736        leader.request_autoscroll(Autoscroll::newest(), cx);
10737        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10738    });
10739    follower
10740        .update(cx, |follower, window, cx| {
10741            follower.apply_update_proto(
10742                &project,
10743                pending_update.borrow_mut().take().unwrap(),
10744                window,
10745                cx,
10746            )
10747        })
10748        .unwrap()
10749        .await
10750        .unwrap();
10751    _ = follower.update(cx, |follower, _, cx| {
10752        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10753        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10754    });
10755    assert!(*is_still_following.borrow());
10756
10757    // Creating a pending selection that precedes another selection
10758    _ = leader.update(cx, |leader, window, cx| {
10759        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10760        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10761    });
10762    follower
10763        .update(cx, |follower, window, cx| {
10764            follower.apply_update_proto(
10765                &project,
10766                pending_update.borrow_mut().take().unwrap(),
10767                window,
10768                cx,
10769            )
10770        })
10771        .unwrap()
10772        .await
10773        .unwrap();
10774    _ = follower.update(cx, |follower, _, cx| {
10775        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10776    });
10777    assert!(*is_still_following.borrow());
10778
10779    // Extend the pending selection so that it surrounds another selection
10780    _ = leader.update(cx, |leader, window, cx| {
10781        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10782    });
10783    follower
10784        .update(cx, |follower, window, cx| {
10785            follower.apply_update_proto(
10786                &project,
10787                pending_update.borrow_mut().take().unwrap(),
10788                window,
10789                cx,
10790            )
10791        })
10792        .unwrap()
10793        .await
10794        .unwrap();
10795    _ = follower.update(cx, |follower, _, cx| {
10796        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10797    });
10798
10799    // Scrolling locally breaks the follow
10800    _ = follower.update(cx, |follower, window, cx| {
10801        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10802        follower.set_scroll_anchor(
10803            ScrollAnchor {
10804                anchor: top_anchor,
10805                offset: gpui::Point::new(0.0, 0.5),
10806            },
10807            window,
10808            cx,
10809        );
10810    });
10811    assert!(!(*is_still_following.borrow()));
10812}
10813
10814#[gpui::test]
10815async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10816    init_test(cx, |_| {});
10817
10818    let fs = FakeFs::new(cx.executor());
10819    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10820    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10821    let pane = workspace
10822        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10823        .unwrap();
10824
10825    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10826
10827    let leader = pane.update_in(cx, |_, window, cx| {
10828        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10829        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10830    });
10831
10832    // Start following the editor when it has no excerpts.
10833    let mut state_message =
10834        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10835    let workspace_entity = workspace.root(cx).unwrap();
10836    let follower_1 = cx
10837        .update_window(*workspace.deref(), |_, window, cx| {
10838            Editor::from_state_proto(
10839                workspace_entity,
10840                ViewId {
10841                    creator: Default::default(),
10842                    id: 0,
10843                },
10844                &mut state_message,
10845                window,
10846                cx,
10847            )
10848        })
10849        .unwrap()
10850        .unwrap()
10851        .await
10852        .unwrap();
10853
10854    let update_message = Rc::new(RefCell::new(None));
10855    follower_1.update_in(cx, {
10856        let update = update_message.clone();
10857        |_, window, cx| {
10858            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10859                leader.read(cx).add_event_to_update_proto(
10860                    event,
10861                    &mut update.borrow_mut(),
10862                    window,
10863                    cx,
10864                );
10865            })
10866            .detach();
10867        }
10868    });
10869
10870    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10871        (
10872            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10873            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10874        )
10875    });
10876
10877    // Insert some excerpts.
10878    leader.update(cx, |leader, cx| {
10879        leader.buffer.update(cx, |multibuffer, cx| {
10880            let excerpt_ids = multibuffer.push_excerpts(
10881                buffer_1.clone(),
10882                [
10883                    ExcerptRange {
10884                        context: 1..6,
10885                        primary: None,
10886                    },
10887                    ExcerptRange {
10888                        context: 12..15,
10889                        primary: None,
10890                    },
10891                    ExcerptRange {
10892                        context: 0..3,
10893                        primary: None,
10894                    },
10895                ],
10896                cx,
10897            );
10898            multibuffer.insert_excerpts_after(
10899                excerpt_ids[0],
10900                buffer_2.clone(),
10901                [
10902                    ExcerptRange {
10903                        context: 8..12,
10904                        primary: None,
10905                    },
10906                    ExcerptRange {
10907                        context: 0..6,
10908                        primary: None,
10909                    },
10910                ],
10911                cx,
10912            );
10913        });
10914    });
10915
10916    // Apply the update of adding the excerpts.
10917    follower_1
10918        .update_in(cx, |follower, window, cx| {
10919            follower.apply_update_proto(
10920                &project,
10921                update_message.borrow().clone().unwrap(),
10922                window,
10923                cx,
10924            )
10925        })
10926        .await
10927        .unwrap();
10928    assert_eq!(
10929        follower_1.update(cx, |editor, cx| editor.text(cx)),
10930        leader.update(cx, |editor, cx| editor.text(cx))
10931    );
10932    update_message.borrow_mut().take();
10933
10934    // Start following separately after it already has excerpts.
10935    let mut state_message =
10936        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10937    let workspace_entity = workspace.root(cx).unwrap();
10938    let follower_2 = cx
10939        .update_window(*workspace.deref(), |_, window, cx| {
10940            Editor::from_state_proto(
10941                workspace_entity,
10942                ViewId {
10943                    creator: Default::default(),
10944                    id: 0,
10945                },
10946                &mut state_message,
10947                window,
10948                cx,
10949            )
10950        })
10951        .unwrap()
10952        .unwrap()
10953        .await
10954        .unwrap();
10955    assert_eq!(
10956        follower_2.update(cx, |editor, cx| editor.text(cx)),
10957        leader.update(cx, |editor, cx| editor.text(cx))
10958    );
10959
10960    // Remove some excerpts.
10961    leader.update(cx, |leader, cx| {
10962        leader.buffer.update(cx, |multibuffer, cx| {
10963            let excerpt_ids = multibuffer.excerpt_ids();
10964            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10965            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10966        });
10967    });
10968
10969    // Apply the update of removing the excerpts.
10970    follower_1
10971        .update_in(cx, |follower, window, cx| {
10972            follower.apply_update_proto(
10973                &project,
10974                update_message.borrow().clone().unwrap(),
10975                window,
10976                cx,
10977            )
10978        })
10979        .await
10980        .unwrap();
10981    follower_2
10982        .update_in(cx, |follower, window, cx| {
10983            follower.apply_update_proto(
10984                &project,
10985                update_message.borrow().clone().unwrap(),
10986                window,
10987                cx,
10988            )
10989        })
10990        .await
10991        .unwrap();
10992    update_message.borrow_mut().take();
10993    assert_eq!(
10994        follower_1.update(cx, |editor, cx| editor.text(cx)),
10995        leader.update(cx, |editor, cx| editor.text(cx))
10996    );
10997}
10998
10999#[gpui::test]
11000async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11001    init_test(cx, |_| {});
11002
11003    let mut cx = EditorTestContext::new(cx).await;
11004    let lsp_store =
11005        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11006
11007    cx.set_state(indoc! {"
11008        ˇfn func(abc def: i32) -> u32 {
11009        }
11010    "});
11011
11012    cx.update(|_, cx| {
11013        lsp_store.update(cx, |lsp_store, cx| {
11014            lsp_store
11015                .update_diagnostics(
11016                    LanguageServerId(0),
11017                    lsp::PublishDiagnosticsParams {
11018                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11019                        version: None,
11020                        diagnostics: vec![
11021                            lsp::Diagnostic {
11022                                range: lsp::Range::new(
11023                                    lsp::Position::new(0, 11),
11024                                    lsp::Position::new(0, 12),
11025                                ),
11026                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11027                                ..Default::default()
11028                            },
11029                            lsp::Diagnostic {
11030                                range: lsp::Range::new(
11031                                    lsp::Position::new(0, 12),
11032                                    lsp::Position::new(0, 15),
11033                                ),
11034                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11035                                ..Default::default()
11036                            },
11037                            lsp::Diagnostic {
11038                                range: lsp::Range::new(
11039                                    lsp::Position::new(0, 25),
11040                                    lsp::Position::new(0, 28),
11041                                ),
11042                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11043                                ..Default::default()
11044                            },
11045                        ],
11046                    },
11047                    &[],
11048                    cx,
11049                )
11050                .unwrap()
11051        });
11052    });
11053
11054    executor.run_until_parked();
11055
11056    cx.update_editor(|editor, window, cx| {
11057        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11058    });
11059
11060    cx.assert_editor_state(indoc! {"
11061        fn func(abc def: i32) -> ˇu32 {
11062        }
11063    "});
11064
11065    cx.update_editor(|editor, window, cx| {
11066        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11067    });
11068
11069    cx.assert_editor_state(indoc! {"
11070        fn func(abc ˇdef: i32) -> u32 {
11071        }
11072    "});
11073
11074    cx.update_editor(|editor, window, cx| {
11075        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11076    });
11077
11078    cx.assert_editor_state(indoc! {"
11079        fn func(abcˇ def: i32) -> u32 {
11080        }
11081    "});
11082
11083    cx.update_editor(|editor, window, cx| {
11084        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11085    });
11086
11087    cx.assert_editor_state(indoc! {"
11088        fn func(abc def: i32) -> ˇu32 {
11089        }
11090    "});
11091}
11092
11093#[gpui::test]
11094async fn cycle_through_same_place_diagnostics(
11095    executor: BackgroundExecutor,
11096    cx: &mut TestAppContext,
11097) {
11098    init_test(cx, |_| {});
11099
11100    let mut cx = EditorTestContext::new(cx).await;
11101    let lsp_store =
11102        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11103
11104    cx.set_state(indoc! {"
11105        ˇfn func(abc def: i32) -> u32 {
11106        }
11107    "});
11108
11109    cx.update(|_, cx| {
11110        lsp_store.update(cx, |lsp_store, cx| {
11111            lsp_store
11112                .update_diagnostics(
11113                    LanguageServerId(0),
11114                    lsp::PublishDiagnosticsParams {
11115                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11116                        version: None,
11117                        diagnostics: vec![
11118                            lsp::Diagnostic {
11119                                range: lsp::Range::new(
11120                                    lsp::Position::new(0, 11),
11121                                    lsp::Position::new(0, 12),
11122                                ),
11123                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11124                                ..Default::default()
11125                            },
11126                            lsp::Diagnostic {
11127                                range: lsp::Range::new(
11128                                    lsp::Position::new(0, 12),
11129                                    lsp::Position::new(0, 15),
11130                                ),
11131                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11132                                ..Default::default()
11133                            },
11134                            lsp::Diagnostic {
11135                                range: lsp::Range::new(
11136                                    lsp::Position::new(0, 12),
11137                                    lsp::Position::new(0, 15),
11138                                ),
11139                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11140                                ..Default::default()
11141                            },
11142                            lsp::Diagnostic {
11143                                range: lsp::Range::new(
11144                                    lsp::Position::new(0, 25),
11145                                    lsp::Position::new(0, 28),
11146                                ),
11147                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11148                                ..Default::default()
11149                            },
11150                        ],
11151                    },
11152                    &[],
11153                    cx,
11154                )
11155                .unwrap()
11156        });
11157    });
11158    executor.run_until_parked();
11159
11160    //// Backward
11161
11162    // Fourth diagnostic
11163    cx.update_editor(|editor, window, cx| {
11164        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11165    });
11166    cx.assert_editor_state(indoc! {"
11167        fn func(abc def: i32) -> ˇu32 {
11168        }
11169    "});
11170
11171    // Third diagnostic
11172    cx.update_editor(|editor, window, cx| {
11173        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11174    });
11175    cx.assert_editor_state(indoc! {"
11176        fn func(abc ˇdef: i32) -> u32 {
11177        }
11178    "});
11179
11180    // Second diagnostic, same place
11181    cx.update_editor(|editor, window, cx| {
11182        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11183    });
11184    cx.assert_editor_state(indoc! {"
11185        fn func(abc ˇdef: i32) -> u32 {
11186        }
11187    "});
11188
11189    // First diagnostic
11190    cx.update_editor(|editor, window, cx| {
11191        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11192    });
11193    cx.assert_editor_state(indoc! {"
11194        fn func(abcˇ def: i32) -> u32 {
11195        }
11196    "});
11197
11198    // Wrapped over, fourth diagnostic
11199    cx.update_editor(|editor, window, cx| {
11200        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11201    });
11202    cx.assert_editor_state(indoc! {"
11203        fn func(abc def: i32) -> ˇu32 {
11204        }
11205    "});
11206
11207    cx.update_editor(|editor, window, cx| {
11208        editor.move_to_beginning(&MoveToBeginning, window, cx);
11209    });
11210    cx.assert_editor_state(indoc! {"
11211        ˇfn func(abc def: i32) -> u32 {
11212        }
11213    "});
11214
11215    //// Forward
11216
11217    // First diagnostic
11218    cx.update_editor(|editor, window, cx| {
11219        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11220    });
11221    cx.assert_editor_state(indoc! {"
11222        fn func(abcˇ def: i32) -> u32 {
11223        }
11224    "});
11225
11226    // Second diagnostic
11227    cx.update_editor(|editor, window, cx| {
11228        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11229    });
11230    cx.assert_editor_state(indoc! {"
11231        fn func(abc ˇdef: i32) -> u32 {
11232        }
11233    "});
11234
11235    // Third diagnostic, same place
11236    cx.update_editor(|editor, window, cx| {
11237        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11238    });
11239    cx.assert_editor_state(indoc! {"
11240        fn func(abc ˇdef: i32) -> u32 {
11241        }
11242    "});
11243
11244    // Fourth diagnostic
11245    cx.update_editor(|editor, window, cx| {
11246        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11247    });
11248    cx.assert_editor_state(indoc! {"
11249        fn func(abc def: i32) -> ˇu32 {
11250        }
11251    "});
11252
11253    // Wrapped around, first diagnostic
11254    cx.update_editor(|editor, window, cx| {
11255        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11256    });
11257    cx.assert_editor_state(indoc! {"
11258        fn func(abcˇ def: i32) -> u32 {
11259        }
11260    "});
11261}
11262
11263#[gpui::test]
11264async fn active_diagnostics_dismiss_after_invalidation(
11265    executor: BackgroundExecutor,
11266    cx: &mut TestAppContext,
11267) {
11268    init_test(cx, |_| {});
11269
11270    let mut cx = EditorTestContext::new(cx).await;
11271    let lsp_store =
11272        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11273
11274    cx.set_state(indoc! {"
11275        ˇfn func(abc def: i32) -> u32 {
11276        }
11277    "});
11278
11279    let message = "Something's wrong!";
11280    cx.update(|_, cx| {
11281        lsp_store.update(cx, |lsp_store, cx| {
11282            lsp_store
11283                .update_diagnostics(
11284                    LanguageServerId(0),
11285                    lsp::PublishDiagnosticsParams {
11286                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11287                        version: None,
11288                        diagnostics: vec![lsp::Diagnostic {
11289                            range: lsp::Range::new(
11290                                lsp::Position::new(0, 11),
11291                                lsp::Position::new(0, 12),
11292                            ),
11293                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11294                            message: message.to_string(),
11295                            ..Default::default()
11296                        }],
11297                    },
11298                    &[],
11299                    cx,
11300                )
11301                .unwrap()
11302        });
11303    });
11304    executor.run_until_parked();
11305
11306    cx.update_editor(|editor, window, cx| {
11307        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11308        assert_eq!(
11309            editor
11310                .active_diagnostics
11311                .as_ref()
11312                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11313            Some(message),
11314            "Should have a diagnostics group activated"
11315        );
11316    });
11317    cx.assert_editor_state(indoc! {"
11318        fn func(abcˇ def: i32) -> u32 {
11319        }
11320    "});
11321
11322    cx.update(|_, cx| {
11323        lsp_store.update(cx, |lsp_store, cx| {
11324            lsp_store
11325                .update_diagnostics(
11326                    LanguageServerId(0),
11327                    lsp::PublishDiagnosticsParams {
11328                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11329                        version: None,
11330                        diagnostics: Vec::new(),
11331                    },
11332                    &[],
11333                    cx,
11334                )
11335                .unwrap()
11336        });
11337    });
11338    executor.run_until_parked();
11339    cx.update_editor(|editor, _, _| {
11340        assert_eq!(
11341            editor.active_diagnostics, None,
11342            "After no diagnostics set to the editor, no diagnostics should be active"
11343        );
11344    });
11345    cx.assert_editor_state(indoc! {"
11346        fn func(abcˇ def: i32) -> u32 {
11347        }
11348    "});
11349
11350    cx.update_editor(|editor, window, cx| {
11351        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11352        assert_eq!(
11353            editor.active_diagnostics, None,
11354            "Should be no diagnostics to go to and activate"
11355        );
11356    });
11357    cx.assert_editor_state(indoc! {"
11358        fn func(abcˇ def: i32) -> u32 {
11359        }
11360    "});
11361}
11362
11363#[gpui::test]
11364async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11365    init_test(cx, |_| {});
11366
11367    let mut cx = EditorTestContext::new(cx).await;
11368
11369    cx.set_state(indoc! {"
11370        fn func(abˇc def: i32) -> u32 {
11371        }
11372    "});
11373    let lsp_store =
11374        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11375
11376    cx.update(|_, cx| {
11377        lsp_store.update(cx, |lsp_store, cx| {
11378            lsp_store.update_diagnostics(
11379                LanguageServerId(0),
11380                lsp::PublishDiagnosticsParams {
11381                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11382                    version: None,
11383                    diagnostics: vec![lsp::Diagnostic {
11384                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11385                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11386                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11387                        ..Default::default()
11388                    }],
11389                },
11390                &[],
11391                cx,
11392            )
11393        })
11394    }).unwrap();
11395    cx.run_until_parked();
11396    cx.update_editor(|editor, window, cx| {
11397        hover_popover::hover(editor, &Default::default(), window, cx)
11398    });
11399    cx.run_until_parked();
11400    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11401}
11402
11403#[gpui::test]
11404async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11405    init_test(cx, |_| {});
11406
11407    let mut cx = EditorTestContext::new(cx).await;
11408
11409    let diff_base = r#"
11410        use some::mod;
11411
11412        const A: u32 = 42;
11413
11414        fn main() {
11415            println!("hello");
11416
11417            println!("world");
11418        }
11419        "#
11420    .unindent();
11421
11422    // Edits are modified, removed, modified, added
11423    cx.set_state(
11424        &r#"
11425        use some::modified;
11426
11427        ˇ
11428        fn main() {
11429            println!("hello there");
11430
11431            println!("around the");
11432            println!("world");
11433        }
11434        "#
11435        .unindent(),
11436    );
11437
11438    cx.set_head_text(&diff_base);
11439    executor.run_until_parked();
11440
11441    cx.update_editor(|editor, window, cx| {
11442        //Wrap around the bottom of the buffer
11443        for _ in 0..3 {
11444            editor.go_to_next_hunk(&GoToHunk, window, cx);
11445        }
11446    });
11447
11448    cx.assert_editor_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.update_editor(|editor, window, cx| {
11464        //Wrap around the top of the buffer
11465        for _ in 0..2 {
11466            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11467        }
11468    });
11469
11470    cx.assert_editor_state(
11471        &r#"
11472        use some::modified;
11473
11474
11475        fn main() {
11476        ˇ    println!("hello there");
11477
11478            println!("around the");
11479            println!("world");
11480        }
11481        "#
11482        .unindent(),
11483    );
11484
11485    cx.update_editor(|editor, window, cx| {
11486        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11487    });
11488
11489    cx.assert_editor_state(
11490        &r#"
11491        use some::modified;
11492
11493        ˇ
11494        fn main() {
11495            println!("hello there");
11496
11497            println!("around the");
11498            println!("world");
11499        }
11500        "#
11501        .unindent(),
11502    );
11503
11504    cx.update_editor(|editor, window, cx| {
11505        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11506    });
11507
11508    cx.assert_editor_state(
11509        &r#"
11510        ˇuse some::modified;
11511
11512
11513        fn main() {
11514            println!("hello there");
11515
11516            println!("around the");
11517            println!("world");
11518        }
11519        "#
11520        .unindent(),
11521    );
11522
11523    cx.update_editor(|editor, window, cx| {
11524        for _ in 0..2 {
11525            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11526        }
11527    });
11528
11529    cx.assert_editor_state(
11530        &r#"
11531        use some::modified;
11532
11533
11534        fn main() {
11535        ˇ    println!("hello there");
11536
11537            println!("around the");
11538            println!("world");
11539        }
11540        "#
11541        .unindent(),
11542    );
11543
11544    cx.update_editor(|editor, window, cx| {
11545        editor.fold(&Fold, window, cx);
11546    });
11547
11548    cx.update_editor(|editor, window, cx| {
11549        editor.go_to_next_hunk(&GoToHunk, window, cx);
11550    });
11551
11552    cx.assert_editor_state(
11553        &r#"
11554        ˇuse some::modified;
11555
11556
11557        fn main() {
11558            println!("hello there");
11559
11560            println!("around the");
11561            println!("world");
11562        }
11563        "#
11564        .unindent(),
11565    );
11566}
11567
11568#[test]
11569fn test_split_words() {
11570    fn split(text: &str) -> Vec<&str> {
11571        split_words(text).collect()
11572    }
11573
11574    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11575    assert_eq!(split("hello_world"), &["hello_", "world"]);
11576    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11577    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11578    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11579    assert_eq!(split("helloworld"), &["helloworld"]);
11580
11581    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11582}
11583
11584#[gpui::test]
11585async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11586    init_test(cx, |_| {});
11587
11588    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11589    let mut assert = |before, after| {
11590        let _state_context = cx.set_state(before);
11591        cx.run_until_parked();
11592        cx.update_editor(|editor, window, cx| {
11593            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11594        });
11595        cx.assert_editor_state(after);
11596    };
11597
11598    // Outside bracket jumps to outside of matching bracket
11599    assert("console.logˇ(var);", "console.log(var)ˇ;");
11600    assert("console.log(var)ˇ;", "console.logˇ(var);");
11601
11602    // Inside bracket jumps to inside of matching bracket
11603    assert("console.log(ˇvar);", "console.log(varˇ);");
11604    assert("console.log(varˇ);", "console.log(ˇvar);");
11605
11606    // When outside a bracket and inside, favor jumping to the inside bracket
11607    assert(
11608        "console.log('foo', [1, 2, 3]ˇ);",
11609        "console.log(ˇ'foo', [1, 2, 3]);",
11610    );
11611    assert(
11612        "console.log(ˇ'foo', [1, 2, 3]);",
11613        "console.log('foo', [1, 2, 3]ˇ);",
11614    );
11615
11616    // Bias forward if two options are equally likely
11617    assert(
11618        "let result = curried_fun()ˇ();",
11619        "let result = curried_fun()()ˇ;",
11620    );
11621
11622    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11623    assert(
11624        indoc! {"
11625            function test() {
11626                console.log('test')ˇ
11627            }"},
11628        indoc! {"
11629            function test() {
11630                console.logˇ('test')
11631            }"},
11632    );
11633}
11634
11635#[gpui::test]
11636async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11637    init_test(cx, |_| {});
11638
11639    let fs = FakeFs::new(cx.executor());
11640    fs.insert_tree(
11641        path!("/a"),
11642        json!({
11643            "main.rs": "fn main() { let a = 5; }",
11644            "other.rs": "// Test file",
11645        }),
11646    )
11647    .await;
11648    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11649
11650    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11651    language_registry.add(Arc::new(Language::new(
11652        LanguageConfig {
11653            name: "Rust".into(),
11654            matcher: LanguageMatcher {
11655                path_suffixes: vec!["rs".to_string()],
11656                ..Default::default()
11657            },
11658            brackets: BracketPairConfig {
11659                pairs: vec![BracketPair {
11660                    start: "{".to_string(),
11661                    end: "}".to_string(),
11662                    close: true,
11663                    surround: true,
11664                    newline: true,
11665                }],
11666                disabled_scopes_by_bracket_ix: Vec::new(),
11667            },
11668            ..Default::default()
11669        },
11670        Some(tree_sitter_rust::LANGUAGE.into()),
11671    )));
11672    let mut fake_servers = language_registry.register_fake_lsp(
11673        "Rust",
11674        FakeLspAdapter {
11675            capabilities: lsp::ServerCapabilities {
11676                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11677                    first_trigger_character: "{".to_string(),
11678                    more_trigger_character: None,
11679                }),
11680                ..Default::default()
11681            },
11682            ..Default::default()
11683        },
11684    );
11685
11686    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11687
11688    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11689
11690    let worktree_id = workspace
11691        .update(cx, |workspace, _, cx| {
11692            workspace.project().update(cx, |project, cx| {
11693                project.worktrees(cx).next().unwrap().read(cx).id()
11694            })
11695        })
11696        .unwrap();
11697
11698    let buffer = project
11699        .update(cx, |project, cx| {
11700            project.open_local_buffer(path!("/a/main.rs"), cx)
11701        })
11702        .await
11703        .unwrap();
11704    let editor_handle = workspace
11705        .update(cx, |workspace, window, cx| {
11706            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11707        })
11708        .unwrap()
11709        .await
11710        .unwrap()
11711        .downcast::<Editor>()
11712        .unwrap();
11713
11714    cx.executor().start_waiting();
11715    let fake_server = fake_servers.next().await.unwrap();
11716
11717    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11718        assert_eq!(
11719            params.text_document_position.text_document.uri,
11720            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11721        );
11722        assert_eq!(
11723            params.text_document_position.position,
11724            lsp::Position::new(0, 21),
11725        );
11726
11727        Ok(Some(vec![lsp::TextEdit {
11728            new_text: "]".to_string(),
11729            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11730        }]))
11731    });
11732
11733    editor_handle.update_in(cx, |editor, window, cx| {
11734        window.focus(&editor.focus_handle(cx));
11735        editor.change_selections(None, window, cx, |s| {
11736            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11737        });
11738        editor.handle_input("{", window, cx);
11739    });
11740
11741    cx.executor().run_until_parked();
11742
11743    buffer.update(cx, |buffer, _| {
11744        assert_eq!(
11745            buffer.text(),
11746            "fn main() { let a = {5}; }",
11747            "No extra braces from on type formatting should appear in the buffer"
11748        )
11749    });
11750}
11751
11752#[gpui::test]
11753async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11754    init_test(cx, |_| {});
11755
11756    let fs = FakeFs::new(cx.executor());
11757    fs.insert_tree(
11758        path!("/a"),
11759        json!({
11760            "main.rs": "fn main() { let a = 5; }",
11761            "other.rs": "// Test file",
11762        }),
11763    )
11764    .await;
11765
11766    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11767
11768    let server_restarts = Arc::new(AtomicUsize::new(0));
11769    let closure_restarts = Arc::clone(&server_restarts);
11770    let language_server_name = "test language server";
11771    let language_name: LanguageName = "Rust".into();
11772
11773    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11774    language_registry.add(Arc::new(Language::new(
11775        LanguageConfig {
11776            name: language_name.clone(),
11777            matcher: LanguageMatcher {
11778                path_suffixes: vec!["rs".to_string()],
11779                ..Default::default()
11780            },
11781            ..Default::default()
11782        },
11783        Some(tree_sitter_rust::LANGUAGE.into()),
11784    )));
11785    let mut fake_servers = language_registry.register_fake_lsp(
11786        "Rust",
11787        FakeLspAdapter {
11788            name: language_server_name,
11789            initialization_options: Some(json!({
11790                "testOptionValue": true
11791            })),
11792            initializer: Some(Box::new(move |fake_server| {
11793                let task_restarts = Arc::clone(&closure_restarts);
11794                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11795                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11796                    futures::future::ready(Ok(()))
11797                });
11798            })),
11799            ..Default::default()
11800        },
11801    );
11802
11803    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11804    let _buffer = project
11805        .update(cx, |project, cx| {
11806            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11807        })
11808        .await
11809        .unwrap();
11810    let _fake_server = fake_servers.next().await.unwrap();
11811    update_test_language_settings(cx, |language_settings| {
11812        language_settings.languages.insert(
11813            language_name.clone(),
11814            LanguageSettingsContent {
11815                tab_size: NonZeroU32::new(8),
11816                ..Default::default()
11817            },
11818        );
11819    });
11820    cx.executor().run_until_parked();
11821    assert_eq!(
11822        server_restarts.load(atomic::Ordering::Acquire),
11823        0,
11824        "Should not restart LSP server on an unrelated change"
11825    );
11826
11827    update_test_project_settings(cx, |project_settings| {
11828        project_settings.lsp.insert(
11829            "Some other server name".into(),
11830            LspSettings {
11831                binary: None,
11832                settings: None,
11833                initialization_options: Some(json!({
11834                    "some other init value": false
11835                })),
11836            },
11837        );
11838    });
11839    cx.executor().run_until_parked();
11840    assert_eq!(
11841        server_restarts.load(atomic::Ordering::Acquire),
11842        0,
11843        "Should not restart LSP server on an unrelated LSP settings change"
11844    );
11845
11846    update_test_project_settings(cx, |project_settings| {
11847        project_settings.lsp.insert(
11848            language_server_name.into(),
11849            LspSettings {
11850                binary: None,
11851                settings: None,
11852                initialization_options: Some(json!({
11853                    "anotherInitValue": false
11854                })),
11855            },
11856        );
11857    });
11858    cx.executor().run_until_parked();
11859    assert_eq!(
11860        server_restarts.load(atomic::Ordering::Acquire),
11861        1,
11862        "Should restart LSP server on a related LSP settings change"
11863    );
11864
11865    update_test_project_settings(cx, |project_settings| {
11866        project_settings.lsp.insert(
11867            language_server_name.into(),
11868            LspSettings {
11869                binary: None,
11870                settings: None,
11871                initialization_options: Some(json!({
11872                    "anotherInitValue": false
11873                })),
11874            },
11875        );
11876    });
11877    cx.executor().run_until_parked();
11878    assert_eq!(
11879        server_restarts.load(atomic::Ordering::Acquire),
11880        1,
11881        "Should not restart LSP server on a related LSP settings change that is the same"
11882    );
11883
11884    update_test_project_settings(cx, |project_settings| {
11885        project_settings.lsp.insert(
11886            language_server_name.into(),
11887            LspSettings {
11888                binary: None,
11889                settings: None,
11890                initialization_options: None,
11891            },
11892        );
11893    });
11894    cx.executor().run_until_parked();
11895    assert_eq!(
11896        server_restarts.load(atomic::Ordering::Acquire),
11897        2,
11898        "Should restart LSP server on another related LSP settings change"
11899    );
11900}
11901
11902#[gpui::test]
11903async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11904    init_test(cx, |_| {});
11905
11906    let mut cx = EditorLspTestContext::new_rust(
11907        lsp::ServerCapabilities {
11908            completion_provider: Some(lsp::CompletionOptions {
11909                trigger_characters: Some(vec![".".to_string()]),
11910                resolve_provider: Some(true),
11911                ..Default::default()
11912            }),
11913            ..Default::default()
11914        },
11915        cx,
11916    )
11917    .await;
11918
11919    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11920    cx.simulate_keystroke(".");
11921    let completion_item = lsp::CompletionItem {
11922        label: "some".into(),
11923        kind: Some(lsp::CompletionItemKind::SNIPPET),
11924        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11925        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11926            kind: lsp::MarkupKind::Markdown,
11927            value: "```rust\nSome(2)\n```".to_string(),
11928        })),
11929        deprecated: Some(false),
11930        sort_text: Some("fffffff2".to_string()),
11931        filter_text: Some("some".to_string()),
11932        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11933        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11934            range: lsp::Range {
11935                start: lsp::Position {
11936                    line: 0,
11937                    character: 22,
11938                },
11939                end: lsp::Position {
11940                    line: 0,
11941                    character: 22,
11942                },
11943            },
11944            new_text: "Some(2)".to_string(),
11945        })),
11946        additional_text_edits: Some(vec![lsp::TextEdit {
11947            range: lsp::Range {
11948                start: lsp::Position {
11949                    line: 0,
11950                    character: 20,
11951                },
11952                end: lsp::Position {
11953                    line: 0,
11954                    character: 22,
11955                },
11956            },
11957            new_text: "".to_string(),
11958        }]),
11959        ..Default::default()
11960    };
11961
11962    let closure_completion_item = completion_item.clone();
11963    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11964        let task_completion_item = closure_completion_item.clone();
11965        async move {
11966            Ok(Some(lsp::CompletionResponse::Array(vec![
11967                task_completion_item,
11968            ])))
11969        }
11970    });
11971
11972    request.next().await;
11973
11974    cx.condition(|editor, _| editor.context_menu_visible())
11975        .await;
11976    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11977        editor
11978            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11979            .unwrap()
11980    });
11981    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11982
11983    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11984        let task_completion_item = completion_item.clone();
11985        async move { Ok(task_completion_item) }
11986    })
11987    .next()
11988    .await
11989    .unwrap();
11990    apply_additional_edits.await.unwrap();
11991    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11992}
11993
11994#[gpui::test]
11995async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11996    init_test(cx, |_| {});
11997
11998    let mut cx = EditorLspTestContext::new_rust(
11999        lsp::ServerCapabilities {
12000            completion_provider: Some(lsp::CompletionOptions {
12001                trigger_characters: Some(vec![".".to_string()]),
12002                resolve_provider: Some(true),
12003                ..Default::default()
12004            }),
12005            ..Default::default()
12006        },
12007        cx,
12008    )
12009    .await;
12010
12011    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12012    cx.simulate_keystroke(".");
12013
12014    let item1 = lsp::CompletionItem {
12015        label: "method id()".to_string(),
12016        filter_text: Some("id".to_string()),
12017        detail: None,
12018        documentation: None,
12019        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12020            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12021            new_text: ".id".to_string(),
12022        })),
12023        ..lsp::CompletionItem::default()
12024    };
12025
12026    let item2 = lsp::CompletionItem {
12027        label: "other".to_string(),
12028        filter_text: Some("other".to_string()),
12029        detail: None,
12030        documentation: None,
12031        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12032            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12033            new_text: ".other".to_string(),
12034        })),
12035        ..lsp::CompletionItem::default()
12036    };
12037
12038    let item1 = item1.clone();
12039    cx.handle_request::<lsp::request::Completion, _, _>({
12040        let item1 = item1.clone();
12041        move |_, _, _| {
12042            let item1 = item1.clone();
12043            let item2 = item2.clone();
12044            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12045        }
12046    })
12047    .next()
12048    .await;
12049
12050    cx.condition(|editor, _| editor.context_menu_visible())
12051        .await;
12052    cx.update_editor(|editor, _, _| {
12053        let context_menu = editor.context_menu.borrow_mut();
12054        let context_menu = context_menu
12055            .as_ref()
12056            .expect("Should have the context menu deployed");
12057        match context_menu {
12058            CodeContextMenu::Completions(completions_menu) => {
12059                let completions = completions_menu.completions.borrow_mut();
12060                assert_eq!(
12061                    completions
12062                        .iter()
12063                        .map(|completion| &completion.label.text)
12064                        .collect::<Vec<_>>(),
12065                    vec!["method id()", "other"]
12066                )
12067            }
12068            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12069        }
12070    });
12071
12072    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12073        let item1 = item1.clone();
12074        move |_, item_to_resolve, _| {
12075            let item1 = item1.clone();
12076            async move {
12077                if item1 == item_to_resolve {
12078                    Ok(lsp::CompletionItem {
12079                        label: "method id()".to_string(),
12080                        filter_text: Some("id".to_string()),
12081                        detail: Some("Now resolved!".to_string()),
12082                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12083                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12084                            range: lsp::Range::new(
12085                                lsp::Position::new(0, 22),
12086                                lsp::Position::new(0, 22),
12087                            ),
12088                            new_text: ".id".to_string(),
12089                        })),
12090                        ..lsp::CompletionItem::default()
12091                    })
12092                } else {
12093                    Ok(item_to_resolve)
12094                }
12095            }
12096        }
12097    })
12098    .next()
12099    .await
12100    .unwrap();
12101    cx.run_until_parked();
12102
12103    cx.update_editor(|editor, window, cx| {
12104        editor.context_menu_next(&Default::default(), window, cx);
12105    });
12106
12107    cx.update_editor(|editor, _, _| {
12108        let context_menu = editor.context_menu.borrow_mut();
12109        let context_menu = context_menu
12110            .as_ref()
12111            .expect("Should have the context menu deployed");
12112        match context_menu {
12113            CodeContextMenu::Completions(completions_menu) => {
12114                let completions = completions_menu.completions.borrow_mut();
12115                assert_eq!(
12116                    completions
12117                        .iter()
12118                        .map(|completion| &completion.label.text)
12119                        .collect::<Vec<_>>(),
12120                    vec!["method id() Now resolved!", "other"],
12121                    "Should update first completion label, but not second as the filter text did not match."
12122                );
12123            }
12124            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12125        }
12126    });
12127}
12128
12129#[gpui::test]
12130async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12131    init_test(cx, |_| {});
12132
12133    let mut cx = EditorLspTestContext::new_rust(
12134        lsp::ServerCapabilities {
12135            completion_provider: Some(lsp::CompletionOptions {
12136                trigger_characters: Some(vec![".".to_string()]),
12137                resolve_provider: Some(true),
12138                ..Default::default()
12139            }),
12140            ..Default::default()
12141        },
12142        cx,
12143    )
12144    .await;
12145
12146    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12147    cx.simulate_keystroke(".");
12148
12149    let unresolved_item_1 = lsp::CompletionItem {
12150        label: "id".to_string(),
12151        filter_text: Some("id".to_string()),
12152        detail: None,
12153        documentation: None,
12154        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12155            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12156            new_text: ".id".to_string(),
12157        })),
12158        ..lsp::CompletionItem::default()
12159    };
12160    let resolved_item_1 = lsp::CompletionItem {
12161        additional_text_edits: Some(vec![lsp::TextEdit {
12162            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12163            new_text: "!!".to_string(),
12164        }]),
12165        ..unresolved_item_1.clone()
12166    };
12167    let unresolved_item_2 = lsp::CompletionItem {
12168        label: "other".to_string(),
12169        filter_text: Some("other".to_string()),
12170        detail: None,
12171        documentation: None,
12172        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12173            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12174            new_text: ".other".to_string(),
12175        })),
12176        ..lsp::CompletionItem::default()
12177    };
12178    let resolved_item_2 = lsp::CompletionItem {
12179        additional_text_edits: Some(vec![lsp::TextEdit {
12180            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12181            new_text: "??".to_string(),
12182        }]),
12183        ..unresolved_item_2.clone()
12184    };
12185
12186    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12187    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12188    cx.lsp
12189        .server
12190        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12191            let unresolved_item_1 = unresolved_item_1.clone();
12192            let resolved_item_1 = resolved_item_1.clone();
12193            let unresolved_item_2 = unresolved_item_2.clone();
12194            let resolved_item_2 = resolved_item_2.clone();
12195            let resolve_requests_1 = resolve_requests_1.clone();
12196            let resolve_requests_2 = resolve_requests_2.clone();
12197            move |unresolved_request, _| {
12198                let unresolved_item_1 = unresolved_item_1.clone();
12199                let resolved_item_1 = resolved_item_1.clone();
12200                let unresolved_item_2 = unresolved_item_2.clone();
12201                let resolved_item_2 = resolved_item_2.clone();
12202                let resolve_requests_1 = resolve_requests_1.clone();
12203                let resolve_requests_2 = resolve_requests_2.clone();
12204                async move {
12205                    if unresolved_request == unresolved_item_1 {
12206                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12207                        Ok(resolved_item_1.clone())
12208                    } else if unresolved_request == unresolved_item_2 {
12209                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12210                        Ok(resolved_item_2.clone())
12211                    } else {
12212                        panic!("Unexpected completion item {unresolved_request:?}")
12213                    }
12214                }
12215            }
12216        })
12217        .detach();
12218
12219    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12220        let unresolved_item_1 = unresolved_item_1.clone();
12221        let unresolved_item_2 = unresolved_item_2.clone();
12222        async move {
12223            Ok(Some(lsp::CompletionResponse::Array(vec![
12224                unresolved_item_1,
12225                unresolved_item_2,
12226            ])))
12227        }
12228    })
12229    .next()
12230    .await;
12231
12232    cx.condition(|editor, _| editor.context_menu_visible())
12233        .await;
12234    cx.update_editor(|editor, _, _| {
12235        let context_menu = editor.context_menu.borrow_mut();
12236        let context_menu = context_menu
12237            .as_ref()
12238            .expect("Should have the context menu deployed");
12239        match context_menu {
12240            CodeContextMenu::Completions(completions_menu) => {
12241                let completions = completions_menu.completions.borrow_mut();
12242                assert_eq!(
12243                    completions
12244                        .iter()
12245                        .map(|completion| &completion.label.text)
12246                        .collect::<Vec<_>>(),
12247                    vec!["id", "other"]
12248                )
12249            }
12250            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12251        }
12252    });
12253    cx.run_until_parked();
12254
12255    cx.update_editor(|editor, window, cx| {
12256        editor.context_menu_next(&ContextMenuNext, window, cx);
12257    });
12258    cx.run_until_parked();
12259    cx.update_editor(|editor, window, cx| {
12260        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12261    });
12262    cx.run_until_parked();
12263    cx.update_editor(|editor, window, cx| {
12264        editor.context_menu_next(&ContextMenuNext, window, cx);
12265    });
12266    cx.run_until_parked();
12267    cx.update_editor(|editor, window, cx| {
12268        editor
12269            .compose_completion(&ComposeCompletion::default(), window, cx)
12270            .expect("No task returned")
12271    })
12272    .await
12273    .expect("Completion failed");
12274    cx.run_until_parked();
12275
12276    cx.update_editor(|editor, _, cx| {
12277        assert_eq!(
12278            resolve_requests_1.load(atomic::Ordering::Acquire),
12279            1,
12280            "Should always resolve once despite multiple selections"
12281        );
12282        assert_eq!(
12283            resolve_requests_2.load(atomic::Ordering::Acquire),
12284            1,
12285            "Should always resolve once after multiple selections and applying the completion"
12286        );
12287        assert_eq!(
12288            editor.text(cx),
12289            "fn main() { let a = ??.other; }",
12290            "Should use resolved data when applying the completion"
12291        );
12292    });
12293}
12294
12295#[gpui::test]
12296async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12297    init_test(cx, |_| {});
12298
12299    let item_0 = lsp::CompletionItem {
12300        label: "abs".into(),
12301        insert_text: Some("abs".into()),
12302        data: Some(json!({ "very": "special"})),
12303        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12304        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12305            lsp::InsertReplaceEdit {
12306                new_text: "abs".to_string(),
12307                insert: lsp::Range::default(),
12308                replace: lsp::Range::default(),
12309            },
12310        )),
12311        ..lsp::CompletionItem::default()
12312    };
12313    let items = iter::once(item_0.clone())
12314        .chain((11..51).map(|i| lsp::CompletionItem {
12315            label: format!("item_{}", i),
12316            insert_text: Some(format!("item_{}", i)),
12317            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12318            ..lsp::CompletionItem::default()
12319        }))
12320        .collect::<Vec<_>>();
12321
12322    let default_commit_characters = vec!["?".to_string()];
12323    let default_data = json!({ "default": "data"});
12324    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12325    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12326    let default_edit_range = lsp::Range {
12327        start: lsp::Position {
12328            line: 0,
12329            character: 5,
12330        },
12331        end: lsp::Position {
12332            line: 0,
12333            character: 5,
12334        },
12335    };
12336
12337    let mut cx = EditorLspTestContext::new_rust(
12338        lsp::ServerCapabilities {
12339            completion_provider: Some(lsp::CompletionOptions {
12340                trigger_characters: Some(vec![".".to_string()]),
12341                resolve_provider: Some(true),
12342                ..Default::default()
12343            }),
12344            ..Default::default()
12345        },
12346        cx,
12347    )
12348    .await;
12349
12350    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12351    cx.simulate_keystroke(".");
12352
12353    let completion_data = default_data.clone();
12354    let completion_characters = default_commit_characters.clone();
12355    let completion_items = items.clone();
12356    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12357        let default_data = completion_data.clone();
12358        let default_commit_characters = completion_characters.clone();
12359        let items = completion_items.clone();
12360        async move {
12361            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12362                items,
12363                item_defaults: Some(lsp::CompletionListItemDefaults {
12364                    data: Some(default_data.clone()),
12365                    commit_characters: Some(default_commit_characters.clone()),
12366                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12367                        default_edit_range,
12368                    )),
12369                    insert_text_format: Some(default_insert_text_format),
12370                    insert_text_mode: Some(default_insert_text_mode),
12371                }),
12372                ..lsp::CompletionList::default()
12373            })))
12374        }
12375    })
12376    .next()
12377    .await;
12378
12379    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12380    cx.lsp
12381        .server
12382        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12383            let closure_resolved_items = resolved_items.clone();
12384            move |item_to_resolve, _| {
12385                let closure_resolved_items = closure_resolved_items.clone();
12386                async move {
12387                    closure_resolved_items.lock().push(item_to_resolve.clone());
12388                    Ok(item_to_resolve)
12389                }
12390            }
12391        })
12392        .detach();
12393
12394    cx.condition(|editor, _| editor.context_menu_visible())
12395        .await;
12396    cx.run_until_parked();
12397    cx.update_editor(|editor, _, _| {
12398        let menu = editor.context_menu.borrow_mut();
12399        match menu.as_ref().expect("should have the completions menu") {
12400            CodeContextMenu::Completions(completions_menu) => {
12401                assert_eq!(
12402                    completions_menu
12403                        .entries
12404                        .borrow()
12405                        .iter()
12406                        .map(|mat| mat.string.clone())
12407                        .collect::<Vec<String>>(),
12408                    items
12409                        .iter()
12410                        .map(|completion| completion.label.clone())
12411                        .collect::<Vec<String>>()
12412                );
12413            }
12414            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12415        }
12416    });
12417    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12418    // with 4 from the end.
12419    assert_eq!(
12420        *resolved_items.lock(),
12421        [&items[0..16], &items[items.len() - 4..items.len()]]
12422            .concat()
12423            .iter()
12424            .cloned()
12425            .map(|mut item| {
12426                if item.data.is_none() {
12427                    item.data = Some(default_data.clone());
12428                }
12429                item
12430            })
12431            .collect::<Vec<lsp::CompletionItem>>(),
12432        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12433    );
12434    resolved_items.lock().clear();
12435
12436    cx.update_editor(|editor, window, cx| {
12437        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12438    });
12439    cx.run_until_parked();
12440    // Completions that have already been resolved are skipped.
12441    assert_eq!(
12442        *resolved_items.lock(),
12443        items[items.len() - 16..items.len() - 4]
12444            .iter()
12445            .cloned()
12446            .map(|mut item| {
12447                if item.data.is_none() {
12448                    item.data = Some(default_data.clone());
12449                }
12450                item
12451            })
12452            .collect::<Vec<lsp::CompletionItem>>()
12453    );
12454    resolved_items.lock().clear();
12455}
12456
12457#[gpui::test]
12458async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12459    init_test(cx, |_| {});
12460
12461    let mut cx = EditorLspTestContext::new(
12462        Language::new(
12463            LanguageConfig {
12464                matcher: LanguageMatcher {
12465                    path_suffixes: vec!["jsx".into()],
12466                    ..Default::default()
12467                },
12468                overrides: [(
12469                    "element".into(),
12470                    LanguageConfigOverride {
12471                        word_characters: Override::Set(['-'].into_iter().collect()),
12472                        ..Default::default()
12473                    },
12474                )]
12475                .into_iter()
12476                .collect(),
12477                ..Default::default()
12478            },
12479            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12480        )
12481        .with_override_query("(jsx_self_closing_element) @element")
12482        .unwrap(),
12483        lsp::ServerCapabilities {
12484            completion_provider: Some(lsp::CompletionOptions {
12485                trigger_characters: Some(vec![":".to_string()]),
12486                ..Default::default()
12487            }),
12488            ..Default::default()
12489        },
12490        cx,
12491    )
12492    .await;
12493
12494    cx.lsp
12495        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12496            Ok(Some(lsp::CompletionResponse::Array(vec![
12497                lsp::CompletionItem {
12498                    label: "bg-blue".into(),
12499                    ..Default::default()
12500                },
12501                lsp::CompletionItem {
12502                    label: "bg-red".into(),
12503                    ..Default::default()
12504                },
12505                lsp::CompletionItem {
12506                    label: "bg-yellow".into(),
12507                    ..Default::default()
12508                },
12509            ])))
12510        });
12511
12512    cx.set_state(r#"<p class="bgˇ" />"#);
12513
12514    // Trigger completion when typing a dash, because the dash is an extra
12515    // word character in the 'element' scope, which contains the cursor.
12516    cx.simulate_keystroke("-");
12517    cx.executor().run_until_parked();
12518    cx.update_editor(|editor, _, _| {
12519        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12520        {
12521            assert_eq!(
12522                completion_menu_entries(&menu),
12523                &["bg-red", "bg-blue", "bg-yellow"]
12524            );
12525        } else {
12526            panic!("expected completion menu to be open");
12527        }
12528    });
12529
12530    cx.simulate_keystroke("l");
12531    cx.executor().run_until_parked();
12532    cx.update_editor(|editor, _, _| {
12533        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12534        {
12535            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12536        } else {
12537            panic!("expected completion menu to be open");
12538        }
12539    });
12540
12541    // When filtering completions, consider the character after the '-' to
12542    // be the start of a subword.
12543    cx.set_state(r#"<p class="yelˇ" />"#);
12544    cx.simulate_keystroke("l");
12545    cx.executor().run_until_parked();
12546    cx.update_editor(|editor, _, _| {
12547        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12548        {
12549            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12550        } else {
12551            panic!("expected completion menu to be open");
12552        }
12553    });
12554}
12555
12556fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12557    let entries = menu.entries.borrow();
12558    entries.iter().map(|mat| mat.string.clone()).collect()
12559}
12560
12561#[gpui::test]
12562async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12563    init_test(cx, |settings| {
12564        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12565            FormatterList(vec![Formatter::Prettier].into()),
12566        ))
12567    });
12568
12569    let fs = FakeFs::new(cx.executor());
12570    fs.insert_file(path!("/file.ts"), Default::default()).await;
12571
12572    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12573    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12574
12575    language_registry.add(Arc::new(Language::new(
12576        LanguageConfig {
12577            name: "TypeScript".into(),
12578            matcher: LanguageMatcher {
12579                path_suffixes: vec!["ts".to_string()],
12580                ..Default::default()
12581            },
12582            ..Default::default()
12583        },
12584        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12585    )));
12586    update_test_language_settings(cx, |settings| {
12587        settings.defaults.prettier = Some(PrettierSettings {
12588            allowed: true,
12589            ..PrettierSettings::default()
12590        });
12591    });
12592
12593    let test_plugin = "test_plugin";
12594    let _ = language_registry.register_fake_lsp(
12595        "TypeScript",
12596        FakeLspAdapter {
12597            prettier_plugins: vec![test_plugin],
12598            ..Default::default()
12599        },
12600    );
12601
12602    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12603    let buffer = project
12604        .update(cx, |project, cx| {
12605            project.open_local_buffer(path!("/file.ts"), cx)
12606        })
12607        .await
12608        .unwrap();
12609
12610    let buffer_text = "one\ntwo\nthree\n";
12611    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12612    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12613    editor.update_in(cx, |editor, window, cx| {
12614        editor.set_text(buffer_text, window, cx)
12615    });
12616
12617    editor
12618        .update_in(cx, |editor, window, cx| {
12619            editor.perform_format(
12620                project.clone(),
12621                FormatTrigger::Manual,
12622                FormatTarget::Buffers,
12623                window,
12624                cx,
12625            )
12626        })
12627        .unwrap()
12628        .await;
12629    assert_eq!(
12630        editor.update(cx, |editor, cx| editor.text(cx)),
12631        buffer_text.to_string() + prettier_format_suffix,
12632        "Test prettier formatting was not applied to the original buffer text",
12633    );
12634
12635    update_test_language_settings(cx, |settings| {
12636        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12637    });
12638    let format = editor.update_in(cx, |editor, window, cx| {
12639        editor.perform_format(
12640            project.clone(),
12641            FormatTrigger::Manual,
12642            FormatTarget::Buffers,
12643            window,
12644            cx,
12645        )
12646    });
12647    format.await.unwrap();
12648    assert_eq!(
12649        editor.update(cx, |editor, cx| editor.text(cx)),
12650        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12651        "Autoformatting (via test prettier) was not applied to the original buffer text",
12652    );
12653}
12654
12655#[gpui::test]
12656async fn test_addition_reverts(cx: &mut TestAppContext) {
12657    init_test(cx, |_| {});
12658    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12659    let base_text = indoc! {r#"
12660        struct Row;
12661        struct Row1;
12662        struct Row2;
12663
12664        struct Row4;
12665        struct Row5;
12666        struct Row6;
12667
12668        struct Row8;
12669        struct Row9;
12670        struct Row10;"#};
12671
12672    // When addition hunks are not adjacent to carets, no hunk revert is performed
12673    assert_hunk_revert(
12674        indoc! {r#"struct Row;
12675                   struct Row1;
12676                   struct Row1.1;
12677                   struct Row1.2;
12678                   struct Row2;ˇ
12679
12680                   struct Row4;
12681                   struct Row5;
12682                   struct Row6;
12683
12684                   struct Row8;
12685                   ˇstruct Row9;
12686                   struct Row9.1;
12687                   struct Row9.2;
12688                   struct Row9.3;
12689                   struct Row10;"#},
12690        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12691        indoc! {r#"struct Row;
12692                   struct Row1;
12693                   struct Row1.1;
12694                   struct Row1.2;
12695                   struct Row2;ˇ
12696
12697                   struct Row4;
12698                   struct Row5;
12699                   struct Row6;
12700
12701                   struct Row8;
12702                   ˇstruct Row9;
12703                   struct Row9.1;
12704                   struct Row9.2;
12705                   struct Row9.3;
12706                   struct Row10;"#},
12707        base_text,
12708        &mut cx,
12709    );
12710    // Same for selections
12711    assert_hunk_revert(
12712        indoc! {r#"struct Row;
12713                   struct Row1;
12714                   struct Row2;
12715                   struct Row2.1;
12716                   struct Row2.2;
12717                   «ˇ
12718                   struct Row4;
12719                   struct» Row5;
12720                   «struct Row6;
12721                   ˇ»
12722                   struct Row9.1;
12723                   struct Row9.2;
12724                   struct Row9.3;
12725                   struct Row8;
12726                   struct Row9;
12727                   struct Row10;"#},
12728        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12729        indoc! {r#"struct Row;
12730                   struct Row1;
12731                   struct Row2;
12732                   struct Row2.1;
12733                   struct Row2.2;
12734                   «ˇ
12735                   struct Row4;
12736                   struct» Row5;
12737                   «struct Row6;
12738                   ˇ»
12739                   struct Row9.1;
12740                   struct Row9.2;
12741                   struct Row9.3;
12742                   struct Row8;
12743                   struct Row9;
12744                   struct Row10;"#},
12745        base_text,
12746        &mut cx,
12747    );
12748
12749    // When carets and selections intersect the addition hunks, those are reverted.
12750    // Adjacent carets got merged.
12751    assert_hunk_revert(
12752        indoc! {r#"struct Row;
12753                   ˇ// something on the top
12754                   struct Row1;
12755                   struct Row2;
12756                   struct Roˇw3.1;
12757                   struct Row2.2;
12758                   struct Row2.3;ˇ
12759
12760                   struct Row4;
12761                   struct ˇRow5.1;
12762                   struct Row5.2;
12763                   struct «Rowˇ»5.3;
12764                   struct Row5;
12765                   struct Row6;
12766                   ˇ
12767                   struct Row9.1;
12768                   struct «Rowˇ»9.2;
12769                   struct «ˇRow»9.3;
12770                   struct Row8;
12771                   struct Row9;
12772                   «ˇ// something on bottom»
12773                   struct Row10;"#},
12774        vec![
12775            DiffHunkStatusKind::Added,
12776            DiffHunkStatusKind::Added,
12777            DiffHunkStatusKind::Added,
12778            DiffHunkStatusKind::Added,
12779            DiffHunkStatusKind::Added,
12780        ],
12781        indoc! {r#"struct Row;
12782                   ˇstruct Row1;
12783                   struct Row2;
12784                   ˇ
12785                   struct Row4;
12786                   ˇstruct Row5;
12787                   struct Row6;
12788                   ˇ
12789                   ˇstruct Row8;
12790                   struct Row9;
12791                   ˇstruct Row10;"#},
12792        base_text,
12793        &mut cx,
12794    );
12795}
12796
12797#[gpui::test]
12798async fn test_modification_reverts(cx: &mut TestAppContext) {
12799    init_test(cx, |_| {});
12800    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12801    let base_text = indoc! {r#"
12802        struct Row;
12803        struct Row1;
12804        struct Row2;
12805
12806        struct Row4;
12807        struct Row5;
12808        struct Row6;
12809
12810        struct Row8;
12811        struct Row9;
12812        struct Row10;"#};
12813
12814    // Modification hunks behave the same as the addition ones.
12815    assert_hunk_revert(
12816        indoc! {r#"struct Row;
12817                   struct Row1;
12818                   struct Row33;
12819                   ˇ
12820                   struct Row4;
12821                   struct Row5;
12822                   struct Row6;
12823                   ˇ
12824                   struct Row99;
12825                   struct Row9;
12826                   struct Row10;"#},
12827        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12828        indoc! {r#"struct Row;
12829                   struct Row1;
12830                   struct Row33;
12831                   ˇ
12832                   struct Row4;
12833                   struct Row5;
12834                   struct Row6;
12835                   ˇ
12836                   struct Row99;
12837                   struct Row9;
12838                   struct Row10;"#},
12839        base_text,
12840        &mut cx,
12841    );
12842    assert_hunk_revert(
12843        indoc! {r#"struct Row;
12844                   struct Row1;
12845                   struct Row33;
12846                   «ˇ
12847                   struct Row4;
12848                   struct» Row5;
12849                   «struct Row6;
12850                   ˇ»
12851                   struct Row99;
12852                   struct Row9;
12853                   struct Row10;"#},
12854        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12855        indoc! {r#"struct Row;
12856                   struct Row1;
12857                   struct Row33;
12858                   «ˇ
12859                   struct Row4;
12860                   struct» Row5;
12861                   «struct Row6;
12862                   ˇ»
12863                   struct Row99;
12864                   struct Row9;
12865                   struct Row10;"#},
12866        base_text,
12867        &mut cx,
12868    );
12869
12870    assert_hunk_revert(
12871        indoc! {r#"ˇstruct Row1.1;
12872                   struct Row1;
12873                   «ˇstr»uct Row22;
12874
12875                   struct ˇRow44;
12876                   struct Row5;
12877                   struct «Rˇ»ow66;ˇ
12878
12879                   «struˇ»ct Row88;
12880                   struct Row9;
12881                   struct Row1011;ˇ"#},
12882        vec![
12883            DiffHunkStatusKind::Modified,
12884            DiffHunkStatusKind::Modified,
12885            DiffHunkStatusKind::Modified,
12886            DiffHunkStatusKind::Modified,
12887            DiffHunkStatusKind::Modified,
12888            DiffHunkStatusKind::Modified,
12889        ],
12890        indoc! {r#"struct Row;
12891                   ˇstruct Row1;
12892                   struct Row2;
12893                   ˇ
12894                   struct Row4;
12895                   ˇstruct Row5;
12896                   struct Row6;
12897                   ˇ
12898                   struct Row8;
12899                   ˇstruct Row9;
12900                   struct Row10;ˇ"#},
12901        base_text,
12902        &mut cx,
12903    );
12904}
12905
12906#[gpui::test]
12907async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12908    init_test(cx, |_| {});
12909    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12910    let base_text = indoc! {r#"
12911        one
12912
12913        two
12914        three
12915        "#};
12916
12917    cx.set_head_text(base_text);
12918    cx.set_state("\nˇ\n");
12919    cx.executor().run_until_parked();
12920    cx.update_editor(|editor, _window, cx| {
12921        editor.expand_selected_diff_hunks(cx);
12922    });
12923    cx.executor().run_until_parked();
12924    cx.update_editor(|editor, window, cx| {
12925        editor.backspace(&Default::default(), window, cx);
12926    });
12927    cx.run_until_parked();
12928    cx.assert_state_with_diff(
12929        indoc! {r#"
12930
12931        - two
12932        - threeˇ
12933        +
12934        "#}
12935        .to_string(),
12936    );
12937}
12938
12939#[gpui::test]
12940async fn test_deletion_reverts(cx: &mut TestAppContext) {
12941    init_test(cx, |_| {});
12942    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12943    let base_text = indoc! {r#"struct Row;
12944struct Row1;
12945struct Row2;
12946
12947struct Row4;
12948struct Row5;
12949struct Row6;
12950
12951struct Row8;
12952struct Row9;
12953struct Row10;"#};
12954
12955    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12956    assert_hunk_revert(
12957        indoc! {r#"struct Row;
12958                   struct Row2;
12959
12960                   ˇstruct Row4;
12961                   struct Row5;
12962                   struct Row6;
12963                   ˇ
12964                   struct Row8;
12965                   struct Row10;"#},
12966        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12967        indoc! {r#"struct Row;
12968                   struct Row2;
12969
12970                   ˇstruct Row4;
12971                   struct Row5;
12972                   struct Row6;
12973                   ˇ
12974                   struct Row8;
12975                   struct Row10;"#},
12976        base_text,
12977        &mut cx,
12978    );
12979    assert_hunk_revert(
12980        indoc! {r#"struct Row;
12981                   struct Row2;
12982
12983                   «ˇstruct Row4;
12984                   struct» Row5;
12985                   «struct Row6;
12986                   ˇ»
12987                   struct Row8;
12988                   struct Row10;"#},
12989        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12990        indoc! {r#"struct Row;
12991                   struct Row2;
12992
12993                   «ˇstruct Row4;
12994                   struct» Row5;
12995                   «struct Row6;
12996                   ˇ»
12997                   struct Row8;
12998                   struct Row10;"#},
12999        base_text,
13000        &mut cx,
13001    );
13002
13003    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
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 Row1;
13017                   ˇstruct Row2;
13018
13019                   struct Row4;
13020                   struct Row5;
13021                   struct Row6;
13022
13023                   struct Row8;ˇ
13024                   struct Row9;
13025                   struct Row10;"#},
13026        base_text,
13027        &mut cx,
13028    );
13029    assert_hunk_revert(
13030        indoc! {r#"struct Row;
13031                   struct Row2«ˇ;
13032                   struct Row4;
13033                   struct» Row5;
13034                   «struct Row6;
13035
13036                   struct Row8;ˇ»
13037                   struct Row10;"#},
13038        vec![
13039            DiffHunkStatusKind::Deleted,
13040            DiffHunkStatusKind::Deleted,
13041            DiffHunkStatusKind::Deleted,
13042        ],
13043        indoc! {r#"struct Row;
13044                   struct Row1;
13045                   struct Row2«ˇ;
13046
13047                   struct Row4;
13048                   struct» Row5;
13049                   «struct Row6;
13050
13051                   struct Row8;ˇ»
13052                   struct Row9;
13053                   struct Row10;"#},
13054        base_text,
13055        &mut cx,
13056    );
13057}
13058
13059#[gpui::test]
13060async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13061    init_test(cx, |_| {});
13062
13063    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13064    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13065    let base_text_3 =
13066        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13067
13068    let text_1 = edit_first_char_of_every_line(base_text_1);
13069    let text_2 = edit_first_char_of_every_line(base_text_2);
13070    let text_3 = edit_first_char_of_every_line(base_text_3);
13071
13072    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13073    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13074    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13075
13076    let multibuffer = cx.new(|cx| {
13077        let mut multibuffer = MultiBuffer::new(ReadWrite);
13078        multibuffer.push_excerpts(
13079            buffer_1.clone(),
13080            [
13081                ExcerptRange {
13082                    context: Point::new(0, 0)..Point::new(3, 0),
13083                    primary: None,
13084                },
13085                ExcerptRange {
13086                    context: Point::new(5, 0)..Point::new(7, 0),
13087                    primary: None,
13088                },
13089                ExcerptRange {
13090                    context: Point::new(9, 0)..Point::new(10, 4),
13091                    primary: None,
13092                },
13093            ],
13094            cx,
13095        );
13096        multibuffer.push_excerpts(
13097            buffer_2.clone(),
13098            [
13099                ExcerptRange {
13100                    context: Point::new(0, 0)..Point::new(3, 0),
13101                    primary: None,
13102                },
13103                ExcerptRange {
13104                    context: Point::new(5, 0)..Point::new(7, 0),
13105                    primary: None,
13106                },
13107                ExcerptRange {
13108                    context: Point::new(9, 0)..Point::new(10, 4),
13109                    primary: None,
13110                },
13111            ],
13112            cx,
13113        );
13114        multibuffer.push_excerpts(
13115            buffer_3.clone(),
13116            [
13117                ExcerptRange {
13118                    context: Point::new(0, 0)..Point::new(3, 0),
13119                    primary: None,
13120                },
13121                ExcerptRange {
13122                    context: Point::new(5, 0)..Point::new(7, 0),
13123                    primary: None,
13124                },
13125                ExcerptRange {
13126                    context: Point::new(9, 0)..Point::new(10, 4),
13127                    primary: None,
13128                },
13129            ],
13130            cx,
13131        );
13132        multibuffer
13133    });
13134
13135    let fs = FakeFs::new(cx.executor());
13136    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13137    let (editor, cx) = cx
13138        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13139    editor.update_in(cx, |editor, _window, cx| {
13140        for (buffer, diff_base) in [
13141            (buffer_1.clone(), base_text_1),
13142            (buffer_2.clone(), base_text_2),
13143            (buffer_3.clone(), base_text_3),
13144        ] {
13145            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13146            editor
13147                .buffer
13148                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13149        }
13150    });
13151    cx.executor().run_until_parked();
13152
13153    editor.update_in(cx, |editor, window, cx| {
13154        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}");
13155        editor.select_all(&SelectAll, window, cx);
13156        editor.git_restore(&Default::default(), window, cx);
13157    });
13158    cx.executor().run_until_parked();
13159
13160    // When all ranges are selected, all buffer hunks are reverted.
13161    editor.update(cx, |editor, cx| {
13162        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");
13163    });
13164    buffer_1.update(cx, |buffer, _| {
13165        assert_eq!(buffer.text(), base_text_1);
13166    });
13167    buffer_2.update(cx, |buffer, _| {
13168        assert_eq!(buffer.text(), base_text_2);
13169    });
13170    buffer_3.update(cx, |buffer, _| {
13171        assert_eq!(buffer.text(), base_text_3);
13172    });
13173
13174    editor.update_in(cx, |editor, window, cx| {
13175        editor.undo(&Default::default(), window, cx);
13176    });
13177
13178    editor.update_in(cx, |editor, window, cx| {
13179        editor.change_selections(None, window, cx, |s| {
13180            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13181        });
13182        editor.git_restore(&Default::default(), window, cx);
13183    });
13184
13185    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13186    // but not affect buffer_2 and its related excerpts.
13187    editor.update(cx, |editor, cx| {
13188        assert_eq!(
13189            editor.text(cx),
13190            "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}"
13191        );
13192    });
13193    buffer_1.update(cx, |buffer, _| {
13194        assert_eq!(buffer.text(), base_text_1);
13195    });
13196    buffer_2.update(cx, |buffer, _| {
13197        assert_eq!(
13198            buffer.text(),
13199            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13200        );
13201    });
13202    buffer_3.update(cx, |buffer, _| {
13203        assert_eq!(
13204            buffer.text(),
13205            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13206        );
13207    });
13208
13209    fn edit_first_char_of_every_line(text: &str) -> String {
13210        text.split('\n')
13211            .map(|line| format!("X{}", &line[1..]))
13212            .collect::<Vec<_>>()
13213            .join("\n")
13214    }
13215}
13216
13217#[gpui::test]
13218async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13219    init_test(cx, |_| {});
13220
13221    let cols = 4;
13222    let rows = 10;
13223    let sample_text_1 = sample_text(rows, cols, 'a');
13224    assert_eq!(
13225        sample_text_1,
13226        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13227    );
13228    let sample_text_2 = sample_text(rows, cols, 'l');
13229    assert_eq!(
13230        sample_text_2,
13231        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13232    );
13233    let sample_text_3 = sample_text(rows, cols, 'v');
13234    assert_eq!(
13235        sample_text_3,
13236        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13237    );
13238
13239    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13240    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13241    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13242
13243    let multi_buffer = cx.new(|cx| {
13244        let mut multibuffer = MultiBuffer::new(ReadWrite);
13245        multibuffer.push_excerpts(
13246            buffer_1.clone(),
13247            [
13248                ExcerptRange {
13249                    context: Point::new(0, 0)..Point::new(3, 0),
13250                    primary: None,
13251                },
13252                ExcerptRange {
13253                    context: Point::new(5, 0)..Point::new(7, 0),
13254                    primary: None,
13255                },
13256                ExcerptRange {
13257                    context: Point::new(9, 0)..Point::new(10, 4),
13258                    primary: None,
13259                },
13260            ],
13261            cx,
13262        );
13263        multibuffer.push_excerpts(
13264            buffer_2.clone(),
13265            [
13266                ExcerptRange {
13267                    context: Point::new(0, 0)..Point::new(3, 0),
13268                    primary: None,
13269                },
13270                ExcerptRange {
13271                    context: Point::new(5, 0)..Point::new(7, 0),
13272                    primary: None,
13273                },
13274                ExcerptRange {
13275                    context: Point::new(9, 0)..Point::new(10, 4),
13276                    primary: None,
13277                },
13278            ],
13279            cx,
13280        );
13281        multibuffer.push_excerpts(
13282            buffer_3.clone(),
13283            [
13284                ExcerptRange {
13285                    context: Point::new(0, 0)..Point::new(3, 0),
13286                    primary: None,
13287                },
13288                ExcerptRange {
13289                    context: Point::new(5, 0)..Point::new(7, 0),
13290                    primary: None,
13291                },
13292                ExcerptRange {
13293                    context: Point::new(9, 0)..Point::new(10, 4),
13294                    primary: None,
13295                },
13296            ],
13297            cx,
13298        );
13299        multibuffer
13300    });
13301
13302    let fs = FakeFs::new(cx.executor());
13303    fs.insert_tree(
13304        "/a",
13305        json!({
13306            "main.rs": sample_text_1,
13307            "other.rs": sample_text_2,
13308            "lib.rs": sample_text_3,
13309        }),
13310    )
13311    .await;
13312    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13313    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13314    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13315    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13316        Editor::new(
13317            EditorMode::Full,
13318            multi_buffer,
13319            Some(project.clone()),
13320            true,
13321            window,
13322            cx,
13323        )
13324    });
13325    let multibuffer_item_id = workspace
13326        .update(cx, |workspace, window, cx| {
13327            assert!(
13328                workspace.active_item(cx).is_none(),
13329                "active item should be None before the first item is added"
13330            );
13331            workspace.add_item_to_active_pane(
13332                Box::new(multi_buffer_editor.clone()),
13333                None,
13334                true,
13335                window,
13336                cx,
13337            );
13338            let active_item = workspace
13339                .active_item(cx)
13340                .expect("should have an active item after adding the multi buffer");
13341            assert!(
13342                !active_item.is_singleton(cx),
13343                "A multi buffer was expected to active after adding"
13344            );
13345            active_item.item_id()
13346        })
13347        .unwrap();
13348    cx.executor().run_until_parked();
13349
13350    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13351        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13352            s.select_ranges(Some(1..2))
13353        });
13354        editor.open_excerpts(&OpenExcerpts, window, cx);
13355    });
13356    cx.executor().run_until_parked();
13357    let first_item_id = workspace
13358        .update(cx, |workspace, window, cx| {
13359            let active_item = workspace
13360                .active_item(cx)
13361                .expect("should have an active item after navigating into the 1st buffer");
13362            let first_item_id = active_item.item_id();
13363            assert_ne!(
13364                first_item_id, multibuffer_item_id,
13365                "Should navigate into the 1st buffer and activate it"
13366            );
13367            assert!(
13368                active_item.is_singleton(cx),
13369                "New active item should be a singleton buffer"
13370            );
13371            assert_eq!(
13372                active_item
13373                    .act_as::<Editor>(cx)
13374                    .expect("should have navigated into an editor for the 1st buffer")
13375                    .read(cx)
13376                    .text(cx),
13377                sample_text_1
13378            );
13379
13380            workspace
13381                .go_back(workspace.active_pane().downgrade(), window, cx)
13382                .detach_and_log_err(cx);
13383
13384            first_item_id
13385        })
13386        .unwrap();
13387    cx.executor().run_until_parked();
13388    workspace
13389        .update(cx, |workspace, _, cx| {
13390            let active_item = workspace
13391                .active_item(cx)
13392                .expect("should have an active item after navigating back");
13393            assert_eq!(
13394                active_item.item_id(),
13395                multibuffer_item_id,
13396                "Should navigate back to the multi buffer"
13397            );
13398            assert!(!active_item.is_singleton(cx));
13399        })
13400        .unwrap();
13401
13402    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13403        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13404            s.select_ranges(Some(39..40))
13405        });
13406        editor.open_excerpts(&OpenExcerpts, window, cx);
13407    });
13408    cx.executor().run_until_parked();
13409    let second_item_id = workspace
13410        .update(cx, |workspace, window, cx| {
13411            let active_item = workspace
13412                .active_item(cx)
13413                .expect("should have an active item after navigating into the 2nd buffer");
13414            let second_item_id = active_item.item_id();
13415            assert_ne!(
13416                second_item_id, multibuffer_item_id,
13417                "Should navigate away from the multibuffer"
13418            );
13419            assert_ne!(
13420                second_item_id, first_item_id,
13421                "Should navigate into the 2nd buffer and activate it"
13422            );
13423            assert!(
13424                active_item.is_singleton(cx),
13425                "New active item should be a singleton buffer"
13426            );
13427            assert_eq!(
13428                active_item
13429                    .act_as::<Editor>(cx)
13430                    .expect("should have navigated into an editor")
13431                    .read(cx)
13432                    .text(cx),
13433                sample_text_2
13434            );
13435
13436            workspace
13437                .go_back(workspace.active_pane().downgrade(), window, cx)
13438                .detach_and_log_err(cx);
13439
13440            second_item_id
13441        })
13442        .unwrap();
13443    cx.executor().run_until_parked();
13444    workspace
13445        .update(cx, |workspace, _, cx| {
13446            let active_item = workspace
13447                .active_item(cx)
13448                .expect("should have an active item after navigating back from the 2nd buffer");
13449            assert_eq!(
13450                active_item.item_id(),
13451                multibuffer_item_id,
13452                "Should navigate back from the 2nd buffer to the multi buffer"
13453            );
13454            assert!(!active_item.is_singleton(cx));
13455        })
13456        .unwrap();
13457
13458    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13459        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13460            s.select_ranges(Some(70..70))
13461        });
13462        editor.open_excerpts(&OpenExcerpts, window, cx);
13463    });
13464    cx.executor().run_until_parked();
13465    workspace
13466        .update(cx, |workspace, window, cx| {
13467            let active_item = workspace
13468                .active_item(cx)
13469                .expect("should have an active item after navigating into the 3rd buffer");
13470            let third_item_id = active_item.item_id();
13471            assert_ne!(
13472                third_item_id, multibuffer_item_id,
13473                "Should navigate into the 3rd buffer and activate it"
13474            );
13475            assert_ne!(third_item_id, first_item_id);
13476            assert_ne!(third_item_id, second_item_id);
13477            assert!(
13478                active_item.is_singleton(cx),
13479                "New active item should be a singleton buffer"
13480            );
13481            assert_eq!(
13482                active_item
13483                    .act_as::<Editor>(cx)
13484                    .expect("should have navigated into an editor")
13485                    .read(cx)
13486                    .text(cx),
13487                sample_text_3
13488            );
13489
13490            workspace
13491                .go_back(workspace.active_pane().downgrade(), window, cx)
13492                .detach_and_log_err(cx);
13493        })
13494        .unwrap();
13495    cx.executor().run_until_parked();
13496    workspace
13497        .update(cx, |workspace, _, cx| {
13498            let active_item = workspace
13499                .active_item(cx)
13500                .expect("should have an active item after navigating back from the 3rd buffer");
13501            assert_eq!(
13502                active_item.item_id(),
13503                multibuffer_item_id,
13504                "Should navigate back from the 3rd buffer to the multi buffer"
13505            );
13506            assert!(!active_item.is_singleton(cx));
13507        })
13508        .unwrap();
13509}
13510
13511#[gpui::test]
13512async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13513    init_test(cx, |_| {});
13514
13515    let mut cx = EditorTestContext::new(cx).await;
13516
13517    let diff_base = r#"
13518        use some::mod;
13519
13520        const A: u32 = 42;
13521
13522        fn main() {
13523            println!("hello");
13524
13525            println!("world");
13526        }
13527        "#
13528    .unindent();
13529
13530    cx.set_state(
13531        &r#"
13532        use some::modified;
13533
13534        ˇ
13535        fn main() {
13536            println!("hello there");
13537
13538            println!("around the");
13539            println!("world");
13540        }
13541        "#
13542        .unindent(),
13543    );
13544
13545    cx.set_head_text(&diff_base);
13546    executor.run_until_parked();
13547
13548    cx.update_editor(|editor, window, cx| {
13549        editor.go_to_next_hunk(&GoToHunk, window, cx);
13550        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13551    });
13552    executor.run_until_parked();
13553    cx.assert_state_with_diff(
13554        r#"
13555          use some::modified;
13556
13557
13558          fn main() {
13559        -     println!("hello");
13560        + ˇ    println!("hello there");
13561
13562              println!("around the");
13563              println!("world");
13564          }
13565        "#
13566        .unindent(),
13567    );
13568
13569    cx.update_editor(|editor, window, cx| {
13570        for _ in 0..2 {
13571            editor.go_to_next_hunk(&GoToHunk, window, cx);
13572            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13573        }
13574    });
13575    executor.run_until_parked();
13576    cx.assert_state_with_diff(
13577        r#"
13578        - use some::mod;
13579        + ˇuse some::modified;
13580
13581
13582          fn main() {
13583        -     println!("hello");
13584        +     println!("hello there");
13585
13586        +     println!("around the");
13587              println!("world");
13588          }
13589        "#
13590        .unindent(),
13591    );
13592
13593    cx.update_editor(|editor, window, cx| {
13594        editor.go_to_next_hunk(&GoToHunk, window, cx);
13595        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13596    });
13597    executor.run_until_parked();
13598    cx.assert_state_with_diff(
13599        r#"
13600        - use some::mod;
13601        + use some::modified;
13602
13603        - const A: u32 = 42;
13604          ˇ
13605          fn main() {
13606        -     println!("hello");
13607        +     println!("hello there");
13608
13609        +     println!("around the");
13610              println!("world");
13611          }
13612        "#
13613        .unindent(),
13614    );
13615
13616    cx.update_editor(|editor, window, cx| {
13617        editor.cancel(&Cancel, window, cx);
13618    });
13619
13620    cx.assert_state_with_diff(
13621        r#"
13622          use some::modified;
13623
13624          ˇ
13625          fn main() {
13626              println!("hello there");
13627
13628              println!("around the");
13629              println!("world");
13630          }
13631        "#
13632        .unindent(),
13633    );
13634}
13635
13636#[gpui::test]
13637async fn test_diff_base_change_with_expanded_diff_hunks(
13638    executor: BackgroundExecutor,
13639    cx: &mut TestAppContext,
13640) {
13641    init_test(cx, |_| {});
13642
13643    let mut cx = EditorTestContext::new(cx).await;
13644
13645    let diff_base = r#"
13646        use some::mod1;
13647        use some::mod2;
13648
13649        const A: u32 = 42;
13650        const B: u32 = 42;
13651        const C: u32 = 42;
13652
13653        fn main() {
13654            println!("hello");
13655
13656            println!("world");
13657        }
13658        "#
13659    .unindent();
13660
13661    cx.set_state(
13662        &r#"
13663        use some::mod2;
13664
13665        const A: u32 = 42;
13666        const C: u32 = 42;
13667
13668        fn main(ˇ) {
13669            //println!("hello");
13670
13671            println!("world");
13672            //
13673            //
13674        }
13675        "#
13676        .unindent(),
13677    );
13678
13679    cx.set_head_text(&diff_base);
13680    executor.run_until_parked();
13681
13682    cx.update_editor(|editor, window, cx| {
13683        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13684    });
13685    executor.run_until_parked();
13686    cx.assert_state_with_diff(
13687        r#"
13688        - use some::mod1;
13689          use some::mod2;
13690
13691          const A: u32 = 42;
13692        - const B: u32 = 42;
13693          const C: u32 = 42;
13694
13695          fn main(ˇ) {
13696        -     println!("hello");
13697        +     //println!("hello");
13698
13699              println!("world");
13700        +     //
13701        +     //
13702          }
13703        "#
13704        .unindent(),
13705    );
13706
13707    cx.set_head_text("new diff base!");
13708    executor.run_until_parked();
13709    cx.assert_state_with_diff(
13710        r#"
13711        - new diff base!
13712        + use some::mod2;
13713        +
13714        + const A: u32 = 42;
13715        + const C: u32 = 42;
13716        +
13717        + fn main(ˇ) {
13718        +     //println!("hello");
13719        +
13720        +     println!("world");
13721        +     //
13722        +     //
13723        + }
13724        "#
13725        .unindent(),
13726    );
13727}
13728
13729#[gpui::test]
13730async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13731    init_test(cx, |_| {});
13732
13733    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13734    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13735    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13736    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13737    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13738    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13739
13740    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13741    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13742    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13743
13744    let multi_buffer = cx.new(|cx| {
13745        let mut multibuffer = MultiBuffer::new(ReadWrite);
13746        multibuffer.push_excerpts(
13747            buffer_1.clone(),
13748            [
13749                ExcerptRange {
13750                    context: Point::new(0, 0)..Point::new(3, 0),
13751                    primary: None,
13752                },
13753                ExcerptRange {
13754                    context: Point::new(5, 0)..Point::new(7, 0),
13755                    primary: None,
13756                },
13757                ExcerptRange {
13758                    context: Point::new(9, 0)..Point::new(10, 3),
13759                    primary: None,
13760                },
13761            ],
13762            cx,
13763        );
13764        multibuffer.push_excerpts(
13765            buffer_2.clone(),
13766            [
13767                ExcerptRange {
13768                    context: Point::new(0, 0)..Point::new(3, 0),
13769                    primary: None,
13770                },
13771                ExcerptRange {
13772                    context: Point::new(5, 0)..Point::new(7, 0),
13773                    primary: None,
13774                },
13775                ExcerptRange {
13776                    context: Point::new(9, 0)..Point::new(10, 3),
13777                    primary: None,
13778                },
13779            ],
13780            cx,
13781        );
13782        multibuffer.push_excerpts(
13783            buffer_3.clone(),
13784            [
13785                ExcerptRange {
13786                    context: Point::new(0, 0)..Point::new(3, 0),
13787                    primary: None,
13788                },
13789                ExcerptRange {
13790                    context: Point::new(5, 0)..Point::new(7, 0),
13791                    primary: None,
13792                },
13793                ExcerptRange {
13794                    context: Point::new(9, 0)..Point::new(10, 3),
13795                    primary: None,
13796                },
13797            ],
13798            cx,
13799        );
13800        multibuffer
13801    });
13802
13803    let editor = cx.add_window(|window, cx| {
13804        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13805    });
13806    editor
13807        .update(cx, |editor, _window, cx| {
13808            for (buffer, diff_base) in [
13809                (buffer_1.clone(), file_1_old),
13810                (buffer_2.clone(), file_2_old),
13811                (buffer_3.clone(), file_3_old),
13812            ] {
13813                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13814                editor
13815                    .buffer
13816                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13817            }
13818        })
13819        .unwrap();
13820
13821    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13822    cx.run_until_parked();
13823
13824    cx.assert_editor_state(
13825        &"
13826            ˇaaa
13827            ccc
13828            ddd
13829
13830            ggg
13831            hhh
13832
13833
13834            lll
13835            mmm
13836            NNN
13837
13838            qqq
13839            rrr
13840
13841            uuu
13842            111
13843            222
13844            333
13845
13846            666
13847            777
13848
13849            000
13850            !!!"
13851        .unindent(),
13852    );
13853
13854    cx.update_editor(|editor, window, cx| {
13855        editor.select_all(&SelectAll, window, cx);
13856        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13857    });
13858    cx.executor().run_until_parked();
13859
13860    cx.assert_state_with_diff(
13861        "
13862            «aaa
13863          - bbb
13864            ccc
13865            ddd
13866
13867            ggg
13868            hhh
13869
13870
13871            lll
13872            mmm
13873          - nnn
13874          + NNN
13875
13876            qqq
13877            rrr
13878
13879            uuu
13880            111
13881            222
13882            333
13883
13884          + 666
13885            777
13886
13887            000
13888            !!!ˇ»"
13889            .unindent(),
13890    );
13891}
13892
13893#[gpui::test]
13894async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13895    init_test(cx, |_| {});
13896
13897    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13898    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13899
13900    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13901    let multi_buffer = cx.new(|cx| {
13902        let mut multibuffer = MultiBuffer::new(ReadWrite);
13903        multibuffer.push_excerpts(
13904            buffer.clone(),
13905            [
13906                ExcerptRange {
13907                    context: Point::new(0, 0)..Point::new(2, 0),
13908                    primary: None,
13909                },
13910                ExcerptRange {
13911                    context: Point::new(4, 0)..Point::new(7, 0),
13912                    primary: None,
13913                },
13914                ExcerptRange {
13915                    context: Point::new(9, 0)..Point::new(10, 0),
13916                    primary: None,
13917                },
13918            ],
13919            cx,
13920        );
13921        multibuffer
13922    });
13923
13924    let editor = cx.add_window(|window, cx| {
13925        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13926    });
13927    editor
13928        .update(cx, |editor, _window, cx| {
13929            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13930            editor
13931                .buffer
13932                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13933        })
13934        .unwrap();
13935
13936    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13937    cx.run_until_parked();
13938
13939    cx.update_editor(|editor, window, cx| {
13940        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13941    });
13942    cx.executor().run_until_parked();
13943
13944    // When the start of a hunk coincides with the start of its excerpt,
13945    // the hunk is expanded. When the start of a a hunk is earlier than
13946    // the start of its excerpt, the hunk is not expanded.
13947    cx.assert_state_with_diff(
13948        "
13949            ˇaaa
13950          - bbb
13951          + BBB
13952
13953          - ddd
13954          - eee
13955          + DDD
13956          + EEE
13957            fff
13958
13959            iii
13960        "
13961        .unindent(),
13962    );
13963}
13964
13965#[gpui::test]
13966async fn test_edits_around_expanded_insertion_hunks(
13967    executor: BackgroundExecutor,
13968    cx: &mut TestAppContext,
13969) {
13970    init_test(cx, |_| {});
13971
13972    let mut cx = EditorTestContext::new(cx).await;
13973
13974    let diff_base = r#"
13975        use some::mod1;
13976        use some::mod2;
13977
13978        const A: u32 = 42;
13979
13980        fn main() {
13981            println!("hello");
13982
13983            println!("world");
13984        }
13985        "#
13986    .unindent();
13987    executor.run_until_parked();
13988    cx.set_state(
13989        &r#"
13990        use some::mod1;
13991        use some::mod2;
13992
13993        const A: u32 = 42;
13994        const B: u32 = 42;
13995        const C: u32 = 42;
13996        ˇ
13997
13998        fn main() {
13999            println!("hello");
14000
14001            println!("world");
14002        }
14003        "#
14004        .unindent(),
14005    );
14006
14007    cx.set_head_text(&diff_base);
14008    executor.run_until_parked();
14009
14010    cx.update_editor(|editor, window, cx| {
14011        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14012    });
14013    executor.run_until_parked();
14014
14015    cx.assert_state_with_diff(
14016        r#"
14017        use some::mod1;
14018        use some::mod2;
14019
14020        const A: u32 = 42;
14021      + const B: u32 = 42;
14022      + const C: u32 = 42;
14023      + ˇ
14024
14025        fn main() {
14026            println!("hello");
14027
14028            println!("world");
14029        }
14030      "#
14031        .unindent(),
14032    );
14033
14034    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14035    executor.run_until_parked();
14036
14037    cx.assert_state_with_diff(
14038        r#"
14039        use some::mod1;
14040        use some::mod2;
14041
14042        const A: u32 = 42;
14043      + const B: u32 = 42;
14044      + const C: u32 = 42;
14045      + const D: u32 = 42;
14046      + ˇ
14047
14048        fn main() {
14049            println!("hello");
14050
14051            println!("world");
14052        }
14053      "#
14054        .unindent(),
14055    );
14056
14057    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14058    executor.run_until_parked();
14059
14060    cx.assert_state_with_diff(
14061        r#"
14062        use some::mod1;
14063        use some::mod2;
14064
14065        const A: u32 = 42;
14066      + const B: u32 = 42;
14067      + const C: u32 = 42;
14068      + const D: u32 = 42;
14069      + const E: u32 = 42;
14070      + ˇ
14071
14072        fn main() {
14073            println!("hello");
14074
14075            println!("world");
14076        }
14077      "#
14078        .unindent(),
14079    );
14080
14081    cx.update_editor(|editor, window, cx| {
14082        editor.delete_line(&DeleteLine, window, cx);
14083    });
14084    executor.run_until_parked();
14085
14086    cx.assert_state_with_diff(
14087        r#"
14088        use some::mod1;
14089        use some::mod2;
14090
14091        const A: u32 = 42;
14092      + const B: u32 = 42;
14093      + const C: u32 = 42;
14094      + const D: u32 = 42;
14095      + const E: u32 = 42;
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.move_up(&MoveUp, window, cx);
14108        editor.delete_line(&DeleteLine, window, cx);
14109        editor.move_up(&MoveUp, window, cx);
14110        editor.delete_line(&DeleteLine, window, cx);
14111        editor.move_up(&MoveUp, window, cx);
14112        editor.delete_line(&DeleteLine, window, cx);
14113    });
14114    executor.run_until_parked();
14115    cx.assert_state_with_diff(
14116        r#"
14117        use some::mod1;
14118        use some::mod2;
14119
14120        const A: u32 = 42;
14121      + const B: u32 = 42;
14122        ˇ
14123        fn main() {
14124            println!("hello");
14125
14126            println!("world");
14127        }
14128      "#
14129        .unindent(),
14130    );
14131
14132    cx.update_editor(|editor, window, cx| {
14133        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14134        editor.delete_line(&DeleteLine, window, cx);
14135    });
14136    executor.run_until_parked();
14137    cx.assert_state_with_diff(
14138        r#"
14139        ˇ
14140        fn main() {
14141            println!("hello");
14142
14143            println!("world");
14144        }
14145      "#
14146        .unindent(),
14147    );
14148}
14149
14150#[gpui::test]
14151async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14152    init_test(cx, |_| {});
14153
14154    let mut cx = EditorTestContext::new(cx).await;
14155    cx.set_head_text(indoc! { "
14156        one
14157        two
14158        three
14159        four
14160        five
14161        "
14162    });
14163    cx.set_state(indoc! { "
14164        one
14165        ˇthree
14166        five
14167    "});
14168    cx.run_until_parked();
14169    cx.update_editor(|editor, window, cx| {
14170        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14171    });
14172    cx.assert_state_with_diff(
14173        indoc! { "
14174        one
14175      - two
14176        ˇthree
14177      - four
14178        five
14179    "}
14180        .to_string(),
14181    );
14182    cx.update_editor(|editor, window, cx| {
14183        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14184    });
14185
14186    cx.assert_state_with_diff(
14187        indoc! { "
14188        one
14189        ˇthree
14190        five
14191    "}
14192        .to_string(),
14193    );
14194
14195    cx.set_state(indoc! { "
14196        one
14197        ˇTWO
14198        three
14199        four
14200        five
14201    "});
14202    cx.run_until_parked();
14203    cx.update_editor(|editor, window, cx| {
14204        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14205    });
14206
14207    cx.assert_state_with_diff(
14208        indoc! { "
14209            one
14210          - two
14211          + ˇTWO
14212            three
14213            four
14214            five
14215        "}
14216        .to_string(),
14217    );
14218    cx.update_editor(|editor, window, cx| {
14219        editor.move_up(&Default::default(), window, cx);
14220        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14221    });
14222    cx.assert_state_with_diff(
14223        indoc! { "
14224            one
14225            ˇTWO
14226            three
14227            four
14228            five
14229        "}
14230        .to_string(),
14231    );
14232}
14233
14234#[gpui::test]
14235async fn test_edits_around_expanded_deletion_hunks(
14236    executor: BackgroundExecutor,
14237    cx: &mut TestAppContext,
14238) {
14239    init_test(cx, |_| {});
14240
14241    let mut cx = EditorTestContext::new(cx).await;
14242
14243    let diff_base = r#"
14244        use some::mod1;
14245        use some::mod2;
14246
14247        const A: u32 = 42;
14248        const B: u32 = 42;
14249        const C: u32 = 42;
14250
14251
14252        fn main() {
14253            println!("hello");
14254
14255            println!("world");
14256        }
14257    "#
14258    .unindent();
14259    executor.run_until_parked();
14260    cx.set_state(
14261        &r#"
14262        use some::mod1;
14263        use some::mod2;
14264
14265        ˇconst B: u32 = 42;
14266        const C: u32 = 42;
14267
14268
14269        fn main() {
14270            println!("hello");
14271
14272            println!("world");
14273        }
14274        "#
14275        .unindent(),
14276    );
14277
14278    cx.set_head_text(&diff_base);
14279    executor.run_until_parked();
14280
14281    cx.update_editor(|editor, window, cx| {
14282        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14283    });
14284    executor.run_until_parked();
14285
14286    cx.assert_state_with_diff(
14287        r#"
14288        use some::mod1;
14289        use some::mod2;
14290
14291      - const A: u32 = 42;
14292        ˇconst B: u32 = 42;
14293        const C: u32 = 42;
14294
14295
14296        fn main() {
14297            println!("hello");
14298
14299            println!("world");
14300        }
14301      "#
14302        .unindent(),
14303    );
14304
14305    cx.update_editor(|editor, window, cx| {
14306        editor.delete_line(&DeleteLine, window, cx);
14307    });
14308    executor.run_until_parked();
14309    cx.assert_state_with_diff(
14310        r#"
14311        use some::mod1;
14312        use some::mod2;
14313
14314      - const A: u32 = 42;
14315      - const B: u32 = 42;
14316        ˇconst C: u32 = 42;
14317
14318
14319        fn main() {
14320            println!("hello");
14321
14322            println!("world");
14323        }
14324      "#
14325        .unindent(),
14326    );
14327
14328    cx.update_editor(|editor, window, cx| {
14329        editor.delete_line(&DeleteLine, window, cx);
14330    });
14331    executor.run_until_parked();
14332    cx.assert_state_with_diff(
14333        r#"
14334        use some::mod1;
14335        use some::mod2;
14336
14337      - const A: u32 = 42;
14338      - const B: u32 = 42;
14339      - const C: u32 = 42;
14340        ˇ
14341
14342        fn main() {
14343            println!("hello");
14344
14345            println!("world");
14346        }
14347      "#
14348        .unindent(),
14349    );
14350
14351    cx.update_editor(|editor, window, cx| {
14352        editor.handle_input("replacement", window, cx);
14353    });
14354    executor.run_until_parked();
14355    cx.assert_state_with_diff(
14356        r#"
14357        use some::mod1;
14358        use some::mod2;
14359
14360      - const A: u32 = 42;
14361      - const B: u32 = 42;
14362      - const C: u32 = 42;
14363      -
14364      + replacementˇ
14365
14366        fn main() {
14367            println!("hello");
14368
14369            println!("world");
14370        }
14371      "#
14372        .unindent(),
14373    );
14374}
14375
14376#[gpui::test]
14377async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14378    init_test(cx, |_| {});
14379
14380    let mut cx = EditorTestContext::new(cx).await;
14381
14382    let base_text = r#"
14383        one
14384        two
14385        three
14386        four
14387        five
14388    "#
14389    .unindent();
14390    executor.run_until_parked();
14391    cx.set_state(
14392        &r#"
14393        one
14394        two
14395        fˇour
14396        five
14397        "#
14398        .unindent(),
14399    );
14400
14401    cx.set_head_text(&base_text);
14402    executor.run_until_parked();
14403
14404    cx.update_editor(|editor, window, cx| {
14405        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14406    });
14407    executor.run_until_parked();
14408
14409    cx.assert_state_with_diff(
14410        r#"
14411          one
14412          two
14413        - three
14414          fˇour
14415          five
14416        "#
14417        .unindent(),
14418    );
14419
14420    cx.update_editor(|editor, window, cx| {
14421        editor.backspace(&Backspace, window, cx);
14422        editor.backspace(&Backspace, window, cx);
14423    });
14424    executor.run_until_parked();
14425    cx.assert_state_with_diff(
14426        r#"
14427          one
14428          two
14429        - threeˇ
14430        - four
14431        + our
14432          five
14433        "#
14434        .unindent(),
14435    );
14436}
14437
14438#[gpui::test]
14439async fn test_edit_after_expanded_modification_hunk(
14440    executor: BackgroundExecutor,
14441    cx: &mut TestAppContext,
14442) {
14443    init_test(cx, |_| {});
14444
14445    let mut cx = EditorTestContext::new(cx).await;
14446
14447    let diff_base = r#"
14448        use some::mod1;
14449        use some::mod2;
14450
14451        const A: u32 = 42;
14452        const B: u32 = 42;
14453        const C: u32 = 42;
14454        const D: u32 = 42;
14455
14456
14457        fn main() {
14458            println!("hello");
14459
14460            println!("world");
14461        }"#
14462    .unindent();
14463
14464    cx.set_state(
14465        &r#"
14466        use some::mod1;
14467        use some::mod2;
14468
14469        const A: u32 = 42;
14470        const B: u32 = 42;
14471        const C: u32 = 43ˇ
14472        const D: u32 = 42;
14473
14474
14475        fn main() {
14476            println!("hello");
14477
14478            println!("world");
14479        }"#
14480        .unindent(),
14481    );
14482
14483    cx.set_head_text(&diff_base);
14484    executor.run_until_parked();
14485    cx.update_editor(|editor, window, cx| {
14486        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14487    });
14488    executor.run_until_parked();
14489
14490    cx.assert_state_with_diff(
14491        r#"
14492        use some::mod1;
14493        use some::mod2;
14494
14495        const A: u32 = 42;
14496        const B: u32 = 42;
14497      - const C: u32 = 42;
14498      + const C: u32 = 43ˇ
14499        const D: u32 = 42;
14500
14501
14502        fn main() {
14503            println!("hello");
14504
14505            println!("world");
14506        }"#
14507        .unindent(),
14508    );
14509
14510    cx.update_editor(|editor, window, cx| {
14511        editor.handle_input("\nnew_line\n", 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      + new_line
14525      + ˇ
14526        const D: u32 = 42;
14527
14528
14529        fn main() {
14530            println!("hello");
14531
14532            println!("world");
14533        }"#
14534        .unindent(),
14535    );
14536}
14537
14538#[gpui::test]
14539async fn test_stage_and_unstage_added_file_hunk(
14540    executor: BackgroundExecutor,
14541    cx: &mut TestAppContext,
14542) {
14543    init_test(cx, |_| {});
14544
14545    let mut cx = EditorTestContext::new(cx).await;
14546    cx.update_editor(|editor, _, cx| {
14547        editor.set_expand_all_diff_hunks(cx);
14548    });
14549
14550    let working_copy = r#"
14551            ˇfn main() {
14552                println!("hello, world!");
14553            }
14554        "#
14555    .unindent();
14556
14557    cx.set_state(&working_copy);
14558    executor.run_until_parked();
14559
14560    cx.assert_state_with_diff(
14561        r#"
14562            + ˇfn main() {
14563            +     println!("hello, world!");
14564            + }
14565        "#
14566        .unindent(),
14567    );
14568    cx.assert_index_text(None);
14569
14570    cx.update_editor(|editor, window, cx| {
14571        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14572    });
14573    executor.run_until_parked();
14574    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14575    cx.assert_state_with_diff(
14576        r#"
14577            + ˇfn main() {
14578            +     println!("hello, world!");
14579            + }
14580        "#
14581        .unindent(),
14582    );
14583
14584    cx.update_editor(|editor, window, cx| {
14585        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14586    });
14587    executor.run_until_parked();
14588    cx.assert_index_text(None);
14589}
14590
14591async fn setup_indent_guides_editor(
14592    text: &str,
14593    cx: &mut TestAppContext,
14594) -> (BufferId, EditorTestContext) {
14595    init_test(cx, |_| {});
14596
14597    let mut cx = EditorTestContext::new(cx).await;
14598
14599    let buffer_id = cx.update_editor(|editor, window, cx| {
14600        editor.set_text(text, window, cx);
14601        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14602
14603        buffer_ids[0]
14604    });
14605
14606    (buffer_id, cx)
14607}
14608
14609fn assert_indent_guides(
14610    range: Range<u32>,
14611    expected: Vec<IndentGuide>,
14612    active_indices: Option<Vec<usize>>,
14613    cx: &mut EditorTestContext,
14614) {
14615    let indent_guides = cx.update_editor(|editor, window, cx| {
14616        let snapshot = editor.snapshot(window, cx).display_snapshot;
14617        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14618            editor,
14619            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14620            true,
14621            &snapshot,
14622            cx,
14623        );
14624
14625        indent_guides.sort_by(|a, b| {
14626            a.depth.cmp(&b.depth).then(
14627                a.start_row
14628                    .cmp(&b.start_row)
14629                    .then(a.end_row.cmp(&b.end_row)),
14630            )
14631        });
14632        indent_guides
14633    });
14634
14635    if let Some(expected) = active_indices {
14636        let active_indices = cx.update_editor(|editor, window, cx| {
14637            let snapshot = editor.snapshot(window, cx).display_snapshot;
14638            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14639        });
14640
14641        assert_eq!(
14642            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14643            expected,
14644            "Active indent guide indices do not match"
14645        );
14646    }
14647
14648    assert_eq!(indent_guides, expected, "Indent guides do not match");
14649}
14650
14651fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14652    IndentGuide {
14653        buffer_id,
14654        start_row: MultiBufferRow(start_row),
14655        end_row: MultiBufferRow(end_row),
14656        depth,
14657        tab_size: 4,
14658        settings: IndentGuideSettings {
14659            enabled: true,
14660            line_width: 1,
14661            active_line_width: 1,
14662            ..Default::default()
14663        },
14664    }
14665}
14666
14667#[gpui::test]
14668async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14669    let (buffer_id, mut cx) = setup_indent_guides_editor(
14670        &"
14671    fn main() {
14672        let a = 1;
14673    }"
14674        .unindent(),
14675        cx,
14676    )
14677    .await;
14678
14679    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14680}
14681
14682#[gpui::test]
14683async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14684    let (buffer_id, mut cx) = setup_indent_guides_editor(
14685        &"
14686    fn main() {
14687        let a = 1;
14688        let b = 2;
14689    }"
14690        .unindent(),
14691        cx,
14692    )
14693    .await;
14694
14695    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14696}
14697
14698#[gpui::test]
14699async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14700    let (buffer_id, mut cx) = setup_indent_guides_editor(
14701        &"
14702    fn main() {
14703        let a = 1;
14704        if a == 3 {
14705            let b = 2;
14706        } else {
14707            let c = 3;
14708        }
14709    }"
14710        .unindent(),
14711        cx,
14712    )
14713    .await;
14714
14715    assert_indent_guides(
14716        0..8,
14717        vec![
14718            indent_guide(buffer_id, 1, 6, 0),
14719            indent_guide(buffer_id, 3, 3, 1),
14720            indent_guide(buffer_id, 5, 5, 1),
14721        ],
14722        None,
14723        &mut cx,
14724    );
14725}
14726
14727#[gpui::test]
14728async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14729    let (buffer_id, mut cx) = setup_indent_guides_editor(
14730        &"
14731    fn main() {
14732        let a = 1;
14733            let b = 2;
14734        let c = 3;
14735    }"
14736        .unindent(),
14737        cx,
14738    )
14739    .await;
14740
14741    assert_indent_guides(
14742        0..5,
14743        vec![
14744            indent_guide(buffer_id, 1, 3, 0),
14745            indent_guide(buffer_id, 2, 2, 1),
14746        ],
14747        None,
14748        &mut cx,
14749    );
14750}
14751
14752#[gpui::test]
14753async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14754    let (buffer_id, mut cx) = setup_indent_guides_editor(
14755        &"
14756        fn main() {
14757            let a = 1;
14758
14759            let c = 3;
14760        }"
14761        .unindent(),
14762        cx,
14763    )
14764    .await;
14765
14766    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14767}
14768
14769#[gpui::test]
14770async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14771    let (buffer_id, mut cx) = setup_indent_guides_editor(
14772        &"
14773        fn main() {
14774            let a = 1;
14775
14776            let c = 3;
14777
14778            if a == 3 {
14779                let b = 2;
14780            } else {
14781                let c = 3;
14782            }
14783        }"
14784        .unindent(),
14785        cx,
14786    )
14787    .await;
14788
14789    assert_indent_guides(
14790        0..11,
14791        vec![
14792            indent_guide(buffer_id, 1, 9, 0),
14793            indent_guide(buffer_id, 6, 6, 1),
14794            indent_guide(buffer_id, 8, 8, 1),
14795        ],
14796        None,
14797        &mut cx,
14798    );
14799}
14800
14801#[gpui::test]
14802async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14803    let (buffer_id, mut cx) = setup_indent_guides_editor(
14804        &"
14805        fn main() {
14806            let a = 1;
14807
14808            let c = 3;
14809
14810            if a == 3 {
14811                let b = 2;
14812            } else {
14813                let c = 3;
14814            }
14815        }"
14816        .unindent(),
14817        cx,
14818    )
14819    .await;
14820
14821    assert_indent_guides(
14822        1..11,
14823        vec![
14824            indent_guide(buffer_id, 1, 9, 0),
14825            indent_guide(buffer_id, 6, 6, 1),
14826            indent_guide(buffer_id, 8, 8, 1),
14827        ],
14828        None,
14829        &mut cx,
14830    );
14831}
14832
14833#[gpui::test]
14834async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14835    let (buffer_id, mut cx) = setup_indent_guides_editor(
14836        &"
14837        fn main() {
14838            let a = 1;
14839
14840            let c = 3;
14841
14842            if a == 3 {
14843                let b = 2;
14844            } else {
14845                let c = 3;
14846            }
14847        }"
14848        .unindent(),
14849        cx,
14850    )
14851    .await;
14852
14853    assert_indent_guides(
14854        1..10,
14855        vec![
14856            indent_guide(buffer_id, 1, 9, 0),
14857            indent_guide(buffer_id, 6, 6, 1),
14858            indent_guide(buffer_id, 8, 8, 1),
14859        ],
14860        None,
14861        &mut cx,
14862    );
14863}
14864
14865#[gpui::test]
14866async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14867    let (buffer_id, mut cx) = setup_indent_guides_editor(
14868        &"
14869        block1
14870            block2
14871                block3
14872                    block4
14873            block2
14874        block1
14875        block1"
14876            .unindent(),
14877        cx,
14878    )
14879    .await;
14880
14881    assert_indent_guides(
14882        1..10,
14883        vec![
14884            indent_guide(buffer_id, 1, 4, 0),
14885            indent_guide(buffer_id, 2, 3, 1),
14886            indent_guide(buffer_id, 3, 3, 2),
14887        ],
14888        None,
14889        &mut cx,
14890    );
14891}
14892
14893#[gpui::test]
14894async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14895    let (buffer_id, mut cx) = setup_indent_guides_editor(
14896        &"
14897        block1
14898            block2
14899                block3
14900
14901        block1
14902        block1"
14903            .unindent(),
14904        cx,
14905    )
14906    .await;
14907
14908    assert_indent_guides(
14909        0..6,
14910        vec![
14911            indent_guide(buffer_id, 1, 2, 0),
14912            indent_guide(buffer_id, 2, 2, 1),
14913        ],
14914        None,
14915        &mut cx,
14916    );
14917}
14918
14919#[gpui::test]
14920async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14921    let (buffer_id, mut cx) = setup_indent_guides_editor(
14922        &"
14923        block1
14924
14925
14926
14927            block2
14928        "
14929        .unindent(),
14930        cx,
14931    )
14932    .await;
14933
14934    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14935}
14936
14937#[gpui::test]
14938async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14939    let (buffer_id, mut cx) = setup_indent_guides_editor(
14940        &"
14941        def a:
14942        \tb = 3
14943        \tif True:
14944        \t\tc = 4
14945        \t\td = 5
14946        \tprint(b)
14947        "
14948        .unindent(),
14949        cx,
14950    )
14951    .await;
14952
14953    assert_indent_guides(
14954        0..6,
14955        vec![
14956            indent_guide(buffer_id, 1, 6, 0),
14957            indent_guide(buffer_id, 3, 4, 1),
14958        ],
14959        None,
14960        &mut cx,
14961    );
14962}
14963
14964#[gpui::test]
14965async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14966    let (buffer_id, mut cx) = setup_indent_guides_editor(
14967        &"
14968    fn main() {
14969        let a = 1;
14970    }"
14971        .unindent(),
14972        cx,
14973    )
14974    .await;
14975
14976    cx.update_editor(|editor, window, cx| {
14977        editor.change_selections(None, window, cx, |s| {
14978            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14979        });
14980    });
14981
14982    assert_indent_guides(
14983        0..3,
14984        vec![indent_guide(buffer_id, 1, 1, 0)],
14985        Some(vec![0]),
14986        &mut cx,
14987    );
14988}
14989
14990#[gpui::test]
14991async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14992    let (buffer_id, mut cx) = setup_indent_guides_editor(
14993        &"
14994    fn main() {
14995        if 1 == 2 {
14996            let a = 1;
14997        }
14998    }"
14999        .unindent(),
15000        cx,
15001    )
15002    .await;
15003
15004    cx.update_editor(|editor, window, cx| {
15005        editor.change_selections(None, window, cx, |s| {
15006            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15007        });
15008    });
15009
15010    assert_indent_guides(
15011        0..4,
15012        vec![
15013            indent_guide(buffer_id, 1, 3, 0),
15014            indent_guide(buffer_id, 2, 2, 1),
15015        ],
15016        Some(vec![1]),
15017        &mut cx,
15018    );
15019
15020    cx.update_editor(|editor, window, cx| {
15021        editor.change_selections(None, window, cx, |s| {
15022            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15023        });
15024    });
15025
15026    assert_indent_guides(
15027        0..4,
15028        vec![
15029            indent_guide(buffer_id, 1, 3, 0),
15030            indent_guide(buffer_id, 2, 2, 1),
15031        ],
15032        Some(vec![1]),
15033        &mut cx,
15034    );
15035
15036    cx.update_editor(|editor, window, cx| {
15037        editor.change_selections(None, window, cx, |s| {
15038            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15039        });
15040    });
15041
15042    assert_indent_guides(
15043        0..4,
15044        vec![
15045            indent_guide(buffer_id, 1, 3, 0),
15046            indent_guide(buffer_id, 2, 2, 1),
15047        ],
15048        Some(vec![0]),
15049        &mut cx,
15050    );
15051}
15052
15053#[gpui::test]
15054async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15055    let (buffer_id, mut cx) = setup_indent_guides_editor(
15056        &"
15057    fn main() {
15058        let a = 1;
15059
15060        let b = 2;
15061    }"
15062        .unindent(),
15063        cx,
15064    )
15065    .await;
15066
15067    cx.update_editor(|editor, window, cx| {
15068        editor.change_selections(None, window, cx, |s| {
15069            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15070        });
15071    });
15072
15073    assert_indent_guides(
15074        0..5,
15075        vec![indent_guide(buffer_id, 1, 3, 0)],
15076        Some(vec![0]),
15077        &mut cx,
15078    );
15079}
15080
15081#[gpui::test]
15082async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15083    let (buffer_id, mut cx) = setup_indent_guides_editor(
15084        &"
15085    def m:
15086        a = 1
15087        pass"
15088            .unindent(),
15089        cx,
15090    )
15091    .await;
15092
15093    cx.update_editor(|editor, window, cx| {
15094        editor.change_selections(None, window, cx, |s| {
15095            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15096        });
15097    });
15098
15099    assert_indent_guides(
15100        0..3,
15101        vec![indent_guide(buffer_id, 1, 2, 0)],
15102        Some(vec![0]),
15103        &mut cx,
15104    );
15105}
15106
15107#[gpui::test]
15108async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15109    init_test(cx, |_| {});
15110    let mut cx = EditorTestContext::new(cx).await;
15111    let text = indoc! {
15112        "
15113        impl A {
15114            fn b() {
15115                0;
15116                3;
15117                5;
15118                6;
15119                7;
15120            }
15121        }
15122        "
15123    };
15124    let base_text = indoc! {
15125        "
15126        impl A {
15127            fn b() {
15128                0;
15129                1;
15130                2;
15131                3;
15132                4;
15133            }
15134            fn c() {
15135                5;
15136                6;
15137                7;
15138            }
15139        }
15140        "
15141    };
15142
15143    cx.update_editor(|editor, window, cx| {
15144        editor.set_text(text, window, cx);
15145
15146        editor.buffer().update(cx, |multibuffer, cx| {
15147            let buffer = multibuffer.as_singleton().unwrap();
15148            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15149
15150            multibuffer.set_all_diff_hunks_expanded(cx);
15151            multibuffer.add_diff(diff, cx);
15152
15153            buffer.read(cx).remote_id()
15154        })
15155    });
15156    cx.run_until_parked();
15157
15158    cx.assert_state_with_diff(
15159        indoc! { "
15160          impl A {
15161              fn b() {
15162                  0;
15163        -         1;
15164        -         2;
15165                  3;
15166        -         4;
15167        -     }
15168        -     fn c() {
15169                  5;
15170                  6;
15171                  7;
15172              }
15173          }
15174          ˇ"
15175        }
15176        .to_string(),
15177    );
15178
15179    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15180        editor
15181            .snapshot(window, cx)
15182            .buffer_snapshot
15183            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15184            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15185            .collect::<Vec<_>>()
15186    });
15187    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15188    assert_eq!(
15189        actual_guides,
15190        vec![
15191            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15192            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15193            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15194        ]
15195    );
15196}
15197
15198#[gpui::test]
15199async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15200    init_test(cx, |_| {});
15201    let mut cx = EditorTestContext::new(cx).await;
15202
15203    let diff_base = r#"
15204        a
15205        b
15206        c
15207        "#
15208    .unindent();
15209
15210    cx.set_state(
15211        &r#"
15212        ˇA
15213        b
15214        C
15215        "#
15216        .unindent(),
15217    );
15218    cx.set_head_text(&diff_base);
15219    cx.update_editor(|editor, window, cx| {
15220        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15221    });
15222    executor.run_until_parked();
15223
15224    let both_hunks_expanded = r#"
15225        - a
15226        + ˇA
15227          b
15228        - c
15229        + C
15230        "#
15231    .unindent();
15232
15233    cx.assert_state_with_diff(both_hunks_expanded.clone());
15234
15235    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15236        let snapshot = editor.snapshot(window, cx);
15237        let hunks = editor
15238            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15239            .collect::<Vec<_>>();
15240        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15241        let buffer_id = hunks[0].buffer_id;
15242        hunks
15243            .into_iter()
15244            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15245            .collect::<Vec<_>>()
15246    });
15247    assert_eq!(hunk_ranges.len(), 2);
15248
15249    cx.update_editor(|editor, _, cx| {
15250        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15251    });
15252    executor.run_until_parked();
15253
15254    let second_hunk_expanded = r#"
15255          ˇA
15256          b
15257        - c
15258        + C
15259        "#
15260    .unindent();
15261
15262    cx.assert_state_with_diff(second_hunk_expanded);
15263
15264    cx.update_editor(|editor, _, cx| {
15265        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15266    });
15267    executor.run_until_parked();
15268
15269    cx.assert_state_with_diff(both_hunks_expanded.clone());
15270
15271    cx.update_editor(|editor, _, cx| {
15272        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15273    });
15274    executor.run_until_parked();
15275
15276    let first_hunk_expanded = r#"
15277        - a
15278        + ˇA
15279          b
15280          C
15281        "#
15282    .unindent();
15283
15284    cx.assert_state_with_diff(first_hunk_expanded);
15285
15286    cx.update_editor(|editor, _, cx| {
15287        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15288    });
15289    executor.run_until_parked();
15290
15291    cx.assert_state_with_diff(both_hunks_expanded);
15292
15293    cx.set_state(
15294        &r#"
15295        ˇA
15296        b
15297        "#
15298        .unindent(),
15299    );
15300    cx.run_until_parked();
15301
15302    // TODO this cursor position seems bad
15303    cx.assert_state_with_diff(
15304        r#"
15305        - ˇa
15306        + A
15307          b
15308        "#
15309        .unindent(),
15310    );
15311
15312    cx.update_editor(|editor, window, cx| {
15313        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15314    });
15315
15316    cx.assert_state_with_diff(
15317        r#"
15318            - ˇa
15319            + A
15320              b
15321            - c
15322            "#
15323        .unindent(),
15324    );
15325
15326    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15327        let snapshot = editor.snapshot(window, cx);
15328        let hunks = editor
15329            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15330            .collect::<Vec<_>>();
15331        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15332        let buffer_id = hunks[0].buffer_id;
15333        hunks
15334            .into_iter()
15335            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15336            .collect::<Vec<_>>()
15337    });
15338    assert_eq!(hunk_ranges.len(), 2);
15339
15340    cx.update_editor(|editor, _, cx| {
15341        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15342    });
15343    executor.run_until_parked();
15344
15345    cx.assert_state_with_diff(
15346        r#"
15347        - ˇa
15348        + A
15349          b
15350        "#
15351        .unindent(),
15352    );
15353}
15354
15355#[gpui::test]
15356async fn test_toggle_deletion_hunk_at_start_of_file(
15357    executor: BackgroundExecutor,
15358    cx: &mut TestAppContext,
15359) {
15360    init_test(cx, |_| {});
15361    let mut cx = EditorTestContext::new(cx).await;
15362
15363    let diff_base = r#"
15364        a
15365        b
15366        c
15367        "#
15368    .unindent();
15369
15370    cx.set_state(
15371        &r#"
15372        ˇb
15373        c
15374        "#
15375        .unindent(),
15376    );
15377    cx.set_head_text(&diff_base);
15378    cx.update_editor(|editor, window, cx| {
15379        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15380    });
15381    executor.run_until_parked();
15382
15383    let hunk_expanded = r#"
15384        - a
15385          ˇb
15386          c
15387        "#
15388    .unindent();
15389
15390    cx.assert_state_with_diff(hunk_expanded.clone());
15391
15392    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15393        let snapshot = editor.snapshot(window, cx);
15394        let hunks = editor
15395            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15396            .collect::<Vec<_>>();
15397        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15398        let buffer_id = hunks[0].buffer_id;
15399        hunks
15400            .into_iter()
15401            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15402            .collect::<Vec<_>>()
15403    });
15404    assert_eq!(hunk_ranges.len(), 1);
15405
15406    cx.update_editor(|editor, _, cx| {
15407        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15408    });
15409    executor.run_until_parked();
15410
15411    let hunk_collapsed = r#"
15412          ˇb
15413          c
15414        "#
15415    .unindent();
15416
15417    cx.assert_state_with_diff(hunk_collapsed);
15418
15419    cx.update_editor(|editor, _, cx| {
15420        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15421    });
15422    executor.run_until_parked();
15423
15424    cx.assert_state_with_diff(hunk_expanded.clone());
15425}
15426
15427#[gpui::test]
15428async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15429    init_test(cx, |_| {});
15430
15431    let fs = FakeFs::new(cx.executor());
15432    fs.insert_tree(
15433        path!("/test"),
15434        json!({
15435            ".git": {},
15436            "file-1": "ONE\n",
15437            "file-2": "TWO\n",
15438            "file-3": "THREE\n",
15439        }),
15440    )
15441    .await;
15442
15443    fs.set_head_for_repo(
15444        path!("/test/.git").as_ref(),
15445        &[
15446            ("file-1".into(), "one\n".into()),
15447            ("file-2".into(), "two\n".into()),
15448            ("file-3".into(), "three\n".into()),
15449        ],
15450    );
15451
15452    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15453    let mut buffers = vec![];
15454    for i in 1..=3 {
15455        let buffer = project
15456            .update(cx, |project, cx| {
15457                let path = format!(path!("/test/file-{}"), i);
15458                project.open_local_buffer(path, cx)
15459            })
15460            .await
15461            .unwrap();
15462        buffers.push(buffer);
15463    }
15464
15465    let multibuffer = cx.new(|cx| {
15466        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15467        multibuffer.set_all_diff_hunks_expanded(cx);
15468        for buffer in &buffers {
15469            let snapshot = buffer.read(cx).snapshot();
15470            multibuffer.set_excerpts_for_path(
15471                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15472                buffer.clone(),
15473                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15474                DEFAULT_MULTIBUFFER_CONTEXT,
15475                cx,
15476            );
15477        }
15478        multibuffer
15479    });
15480
15481    let editor = cx.add_window(|window, cx| {
15482        Editor::new(
15483            EditorMode::Full,
15484            multibuffer,
15485            Some(project),
15486            true,
15487            window,
15488            cx,
15489        )
15490    });
15491    cx.run_until_parked();
15492
15493    let snapshot = editor
15494        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15495        .unwrap();
15496    let hunks = snapshot
15497        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15498        .map(|hunk| match hunk {
15499            DisplayDiffHunk::Unfolded {
15500                display_row_range, ..
15501            } => display_row_range,
15502            DisplayDiffHunk::Folded { .. } => unreachable!(),
15503        })
15504        .collect::<Vec<_>>();
15505    assert_eq!(
15506        hunks,
15507        [
15508            DisplayRow(3)..DisplayRow(5),
15509            DisplayRow(10)..DisplayRow(12),
15510            DisplayRow(17)..DisplayRow(19),
15511        ]
15512    );
15513}
15514
15515#[gpui::test]
15516async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15517    init_test(cx, |_| {});
15518
15519    let mut cx = EditorTestContext::new(cx).await;
15520    cx.set_head_text(indoc! { "
15521        one
15522        two
15523        three
15524        four
15525        five
15526        "
15527    });
15528    cx.set_index_text(indoc! { "
15529        one
15530        two
15531        three
15532        four
15533        five
15534        "
15535    });
15536    cx.set_state(indoc! {"
15537        one
15538        TWO
15539        ˇTHREE
15540        FOUR
15541        five
15542    "});
15543    cx.run_until_parked();
15544    cx.update_editor(|editor, window, cx| {
15545        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15546    });
15547    cx.run_until_parked();
15548    cx.assert_index_text(Some(indoc! {"
15549        one
15550        TWO
15551        THREE
15552        FOUR
15553        five
15554    "}));
15555    cx.set_state(indoc! { "
15556        one
15557        TWO
15558        ˇTHREE-HUNDRED
15559        FOUR
15560        five
15561    "});
15562    cx.run_until_parked();
15563    cx.update_editor(|editor, window, cx| {
15564        let snapshot = editor.snapshot(window, cx);
15565        let hunks = editor
15566            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15567            .collect::<Vec<_>>();
15568        assert_eq!(hunks.len(), 1);
15569        assert_eq!(
15570            hunks[0].status(),
15571            DiffHunkStatus {
15572                kind: DiffHunkStatusKind::Modified,
15573                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15574            }
15575        );
15576
15577        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15578    });
15579    cx.run_until_parked();
15580    cx.assert_index_text(Some(indoc! {"
15581        one
15582        TWO
15583        THREE-HUNDRED
15584        FOUR
15585        five
15586    "}));
15587}
15588
15589#[gpui::test]
15590fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15591    init_test(cx, |_| {});
15592
15593    let editor = cx.add_window(|window, cx| {
15594        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15595        build_editor(buffer, window, cx)
15596    });
15597
15598    let render_args = Arc::new(Mutex::new(None));
15599    let snapshot = editor
15600        .update(cx, |editor, window, cx| {
15601            let snapshot = editor.buffer().read(cx).snapshot(cx);
15602            let range =
15603                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15604
15605            struct RenderArgs {
15606                row: MultiBufferRow,
15607                folded: bool,
15608                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15609            }
15610
15611            let crease = Crease::inline(
15612                range,
15613                FoldPlaceholder::test(),
15614                {
15615                    let toggle_callback = render_args.clone();
15616                    move |row, folded, callback, _window, _cx| {
15617                        *toggle_callback.lock() = Some(RenderArgs {
15618                            row,
15619                            folded,
15620                            callback,
15621                        });
15622                        div()
15623                    }
15624                },
15625                |_row, _folded, _window, _cx| div(),
15626            );
15627
15628            editor.insert_creases(Some(crease), cx);
15629            let snapshot = editor.snapshot(window, cx);
15630            let _div = snapshot.render_crease_toggle(
15631                MultiBufferRow(1),
15632                false,
15633                cx.entity().clone(),
15634                window,
15635                cx,
15636            );
15637            snapshot
15638        })
15639        .unwrap();
15640
15641    let render_args = render_args.lock().take().unwrap();
15642    assert_eq!(render_args.row, MultiBufferRow(1));
15643    assert!(!render_args.folded);
15644    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15645
15646    cx.update_window(*editor, |_, window, cx| {
15647        (render_args.callback)(true, window, cx)
15648    })
15649    .unwrap();
15650    let snapshot = editor
15651        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15652        .unwrap();
15653    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15654
15655    cx.update_window(*editor, |_, window, cx| {
15656        (render_args.callback)(false, window, cx)
15657    })
15658    .unwrap();
15659    let snapshot = editor
15660        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15661        .unwrap();
15662    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15663}
15664
15665#[gpui::test]
15666async fn test_input_text(cx: &mut TestAppContext) {
15667    init_test(cx, |_| {});
15668    let mut cx = EditorTestContext::new(cx).await;
15669
15670    cx.set_state(
15671        &r#"ˇone
15672        two
15673
15674        three
15675        fourˇ
15676        five
15677
15678        siˇx"#
15679            .unindent(),
15680    );
15681
15682    cx.dispatch_action(HandleInput(String::new()));
15683    cx.assert_editor_state(
15684        &r#"ˇone
15685        two
15686
15687        three
15688        fourˇ
15689        five
15690
15691        siˇx"#
15692            .unindent(),
15693    );
15694
15695    cx.dispatch_action(HandleInput("AAAA".to_string()));
15696    cx.assert_editor_state(
15697        &r#"AAAAˇone
15698        two
15699
15700        three
15701        fourAAAAˇ
15702        five
15703
15704        siAAAAˇx"#
15705            .unindent(),
15706    );
15707}
15708
15709#[gpui::test]
15710async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15711    init_test(cx, |_| {});
15712
15713    let mut cx = EditorTestContext::new(cx).await;
15714    cx.set_state(
15715        r#"let foo = 1;
15716let foo = 2;
15717let foo = 3;
15718let fooˇ = 4;
15719let foo = 5;
15720let foo = 6;
15721let foo = 7;
15722let foo = 8;
15723let foo = 9;
15724let foo = 10;
15725let foo = 11;
15726let foo = 12;
15727let foo = 13;
15728let foo = 14;
15729let foo = 15;"#,
15730    );
15731
15732    cx.update_editor(|e, window, cx| {
15733        assert_eq!(
15734            e.next_scroll_position,
15735            NextScrollCursorCenterTopBottom::Center,
15736            "Default next scroll direction is center",
15737        );
15738
15739        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15740        assert_eq!(
15741            e.next_scroll_position,
15742            NextScrollCursorCenterTopBottom::Top,
15743            "After center, next scroll direction should be top",
15744        );
15745
15746        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15747        assert_eq!(
15748            e.next_scroll_position,
15749            NextScrollCursorCenterTopBottom::Bottom,
15750            "After top, next scroll direction should be bottom",
15751        );
15752
15753        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15754        assert_eq!(
15755            e.next_scroll_position,
15756            NextScrollCursorCenterTopBottom::Center,
15757            "After bottom, scrolling should start over",
15758        );
15759
15760        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15761        assert_eq!(
15762            e.next_scroll_position,
15763            NextScrollCursorCenterTopBottom::Top,
15764            "Scrolling continues if retriggered fast enough"
15765        );
15766    });
15767
15768    cx.executor()
15769        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15770    cx.executor().run_until_parked();
15771    cx.update_editor(|e, _, _| {
15772        assert_eq!(
15773            e.next_scroll_position,
15774            NextScrollCursorCenterTopBottom::Center,
15775            "If scrolling is not triggered fast enough, it should reset"
15776        );
15777    });
15778}
15779
15780#[gpui::test]
15781async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15782    init_test(cx, |_| {});
15783    let mut cx = EditorLspTestContext::new_rust(
15784        lsp::ServerCapabilities {
15785            definition_provider: Some(lsp::OneOf::Left(true)),
15786            references_provider: Some(lsp::OneOf::Left(true)),
15787            ..lsp::ServerCapabilities::default()
15788        },
15789        cx,
15790    )
15791    .await;
15792
15793    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15794        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15795            move |params, _| async move {
15796                if empty_go_to_definition {
15797                    Ok(None)
15798                } else {
15799                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15800                        uri: params.text_document_position_params.text_document.uri,
15801                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15802                    })))
15803                }
15804            },
15805        );
15806        let references =
15807            cx.lsp
15808                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15809                    Ok(Some(vec![lsp::Location {
15810                        uri: params.text_document_position.text_document.uri,
15811                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15812                    }]))
15813                });
15814        (go_to_definition, references)
15815    };
15816
15817    cx.set_state(
15818        &r#"fn one() {
15819            let mut a = ˇtwo();
15820        }
15821
15822        fn two() {}"#
15823            .unindent(),
15824    );
15825    set_up_lsp_handlers(false, &mut cx);
15826    let navigated = cx
15827        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15828        .await
15829        .expect("Failed to navigate to definition");
15830    assert_eq!(
15831        navigated,
15832        Navigated::Yes,
15833        "Should have navigated to definition from the GetDefinition response"
15834    );
15835    cx.assert_editor_state(
15836        &r#"fn one() {
15837            let mut a = two();
15838        }
15839
15840        fn «twoˇ»() {}"#
15841            .unindent(),
15842    );
15843
15844    let editors = cx.update_workspace(|workspace, _, cx| {
15845        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15846    });
15847    cx.update_editor(|_, _, test_editor_cx| {
15848        assert_eq!(
15849            editors.len(),
15850            1,
15851            "Initially, only one, test, editor should be open in the workspace"
15852        );
15853        assert_eq!(
15854            test_editor_cx.entity(),
15855            editors.last().expect("Asserted len is 1").clone()
15856        );
15857    });
15858
15859    set_up_lsp_handlers(true, &mut cx);
15860    let navigated = cx
15861        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15862        .await
15863        .expect("Failed to navigate to lookup references");
15864    assert_eq!(
15865        navigated,
15866        Navigated::Yes,
15867        "Should have navigated to references as a fallback after empty GoToDefinition response"
15868    );
15869    // We should not change the selections in the existing file,
15870    // if opening another milti buffer with the references
15871    cx.assert_editor_state(
15872        &r#"fn one() {
15873            let mut a = two();
15874        }
15875
15876        fn «twoˇ»() {}"#
15877            .unindent(),
15878    );
15879    let editors = cx.update_workspace(|workspace, _, cx| {
15880        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15881    });
15882    cx.update_editor(|_, _, test_editor_cx| {
15883        assert_eq!(
15884            editors.len(),
15885            2,
15886            "After falling back to references search, we open a new editor with the results"
15887        );
15888        let references_fallback_text = editors
15889            .into_iter()
15890            .find(|new_editor| *new_editor != test_editor_cx.entity())
15891            .expect("Should have one non-test editor now")
15892            .read(test_editor_cx)
15893            .text(test_editor_cx);
15894        assert_eq!(
15895            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15896            "Should use the range from the references response and not the GoToDefinition one"
15897        );
15898    });
15899}
15900
15901#[gpui::test]
15902async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15903    init_test(cx, |_| {});
15904
15905    let language = Arc::new(Language::new(
15906        LanguageConfig::default(),
15907        Some(tree_sitter_rust::LANGUAGE.into()),
15908    ));
15909
15910    let text = r#"
15911        #[cfg(test)]
15912        mod tests() {
15913            #[test]
15914            fn runnable_1() {
15915                let a = 1;
15916            }
15917
15918            #[test]
15919            fn runnable_2() {
15920                let a = 1;
15921                let b = 2;
15922            }
15923        }
15924    "#
15925    .unindent();
15926
15927    let fs = FakeFs::new(cx.executor());
15928    fs.insert_file("/file.rs", Default::default()).await;
15929
15930    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15931    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15932    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15933    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15934    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15935
15936    let editor = cx.new_window_entity(|window, cx| {
15937        Editor::new(
15938            EditorMode::Full,
15939            multi_buffer,
15940            Some(project.clone()),
15941            true,
15942            window,
15943            cx,
15944        )
15945    });
15946
15947    editor.update_in(cx, |editor, window, cx| {
15948        let snapshot = editor.buffer().read(cx).snapshot(cx);
15949        editor.tasks.insert(
15950            (buffer.read(cx).remote_id(), 3),
15951            RunnableTasks {
15952                templates: vec![],
15953                offset: snapshot.anchor_before(43),
15954                column: 0,
15955                extra_variables: HashMap::default(),
15956                context_range: BufferOffset(43)..BufferOffset(85),
15957            },
15958        );
15959        editor.tasks.insert(
15960            (buffer.read(cx).remote_id(), 8),
15961            RunnableTasks {
15962                templates: vec![],
15963                offset: snapshot.anchor_before(86),
15964                column: 0,
15965                extra_variables: HashMap::default(),
15966                context_range: BufferOffset(86)..BufferOffset(191),
15967            },
15968        );
15969
15970        // Test finding task when cursor is inside function body
15971        editor.change_selections(None, window, cx, |s| {
15972            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15973        });
15974        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15975        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15976
15977        // Test finding task when cursor is on function name
15978        editor.change_selections(None, window, cx, |s| {
15979            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15980        });
15981        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15982        assert_eq!(row, 8, "Should find task when cursor is on function name");
15983    });
15984}
15985
15986#[gpui::test]
15987async fn test_folding_buffers(cx: &mut TestAppContext) {
15988    init_test(cx, |_| {});
15989
15990    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15991    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15992    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15993
15994    let fs = FakeFs::new(cx.executor());
15995    fs.insert_tree(
15996        path!("/a"),
15997        json!({
15998            "first.rs": sample_text_1,
15999            "second.rs": sample_text_2,
16000            "third.rs": sample_text_3,
16001        }),
16002    )
16003    .await;
16004    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16005    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16006    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16007    let worktree = project.update(cx, |project, cx| {
16008        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16009        assert_eq!(worktrees.len(), 1);
16010        worktrees.pop().unwrap()
16011    });
16012    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16013
16014    let buffer_1 = project
16015        .update(cx, |project, cx| {
16016            project.open_buffer((worktree_id, "first.rs"), cx)
16017        })
16018        .await
16019        .unwrap();
16020    let buffer_2 = project
16021        .update(cx, |project, cx| {
16022            project.open_buffer((worktree_id, "second.rs"), cx)
16023        })
16024        .await
16025        .unwrap();
16026    let buffer_3 = project
16027        .update(cx, |project, cx| {
16028            project.open_buffer((worktree_id, "third.rs"), cx)
16029        })
16030        .await
16031        .unwrap();
16032
16033    let multi_buffer = cx.new(|cx| {
16034        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16035        multi_buffer.push_excerpts(
16036            buffer_1.clone(),
16037            [
16038                ExcerptRange {
16039                    context: Point::new(0, 0)..Point::new(3, 0),
16040                    primary: None,
16041                },
16042                ExcerptRange {
16043                    context: Point::new(5, 0)..Point::new(7, 0),
16044                    primary: None,
16045                },
16046                ExcerptRange {
16047                    context: Point::new(9, 0)..Point::new(10, 4),
16048                    primary: None,
16049                },
16050            ],
16051            cx,
16052        );
16053        multi_buffer.push_excerpts(
16054            buffer_2.clone(),
16055            [
16056                ExcerptRange {
16057                    context: Point::new(0, 0)..Point::new(3, 0),
16058                    primary: None,
16059                },
16060                ExcerptRange {
16061                    context: Point::new(5, 0)..Point::new(7, 0),
16062                    primary: None,
16063                },
16064                ExcerptRange {
16065                    context: Point::new(9, 0)..Point::new(10, 4),
16066                    primary: None,
16067                },
16068            ],
16069            cx,
16070        );
16071        multi_buffer.push_excerpts(
16072            buffer_3.clone(),
16073            [
16074                ExcerptRange {
16075                    context: Point::new(0, 0)..Point::new(3, 0),
16076                    primary: None,
16077                },
16078                ExcerptRange {
16079                    context: Point::new(5, 0)..Point::new(7, 0),
16080                    primary: None,
16081                },
16082                ExcerptRange {
16083                    context: Point::new(9, 0)..Point::new(10, 4),
16084                    primary: None,
16085                },
16086            ],
16087            cx,
16088        );
16089        multi_buffer
16090    });
16091    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16092        Editor::new(
16093            EditorMode::Full,
16094            multi_buffer.clone(),
16095            Some(project.clone()),
16096            true,
16097            window,
16098            cx,
16099        )
16100    });
16101
16102    assert_eq!(
16103        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16104        "\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",
16105    );
16106
16107    multi_buffer_editor.update(cx, |editor, cx| {
16108        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16109    });
16110    assert_eq!(
16111        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16112        "\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",
16113        "After folding the first buffer, its text should not be displayed"
16114    );
16115
16116    multi_buffer_editor.update(cx, |editor, cx| {
16117        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16118    });
16119    assert_eq!(
16120        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16121        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
16122        "After folding the second buffer, its text should not be displayed"
16123    );
16124
16125    multi_buffer_editor.update(cx, |editor, cx| {
16126        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16127    });
16128    assert_eq!(
16129        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16130        "\n\n\n\n\n",
16131        "After folding the third buffer, its text should not be displayed"
16132    );
16133
16134    // Emulate selection inside the fold logic, that should work
16135    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16136        editor
16137            .snapshot(window, cx)
16138            .next_line_boundary(Point::new(0, 4));
16139    });
16140
16141    multi_buffer_editor.update(cx, |editor, cx| {
16142        editor.unfold_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\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
16147        "After unfolding the second buffer, its text should be displayed"
16148    );
16149
16150    // Typing inside of buffer 1 causes that buffer to be unfolded.
16151    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16152        assert_eq!(
16153            multi_buffer
16154                .read(cx)
16155                .snapshot(cx)
16156                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16157                .collect::<String>(),
16158            "bbbb"
16159        );
16160        editor.change_selections(None, window, cx, |selections| {
16161            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16162        });
16163        editor.handle_input("B", window, cx);
16164    });
16165
16166    assert_eq!(
16167        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16168        "\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",
16169        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16170    );
16171
16172    multi_buffer_editor.update(cx, |editor, cx| {
16173        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16174    });
16175    assert_eq!(
16176        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16177        "\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",
16178        "After unfolding the all buffers, all original text should be displayed"
16179    );
16180}
16181
16182#[gpui::test]
16183async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16184    init_test(cx, |_| {});
16185
16186    let sample_text_1 = "1111\n2222\n3333".to_string();
16187    let sample_text_2 = "4444\n5555\n6666".to_string();
16188    let sample_text_3 = "7777\n8888\n9999".to_string();
16189
16190    let fs = FakeFs::new(cx.executor());
16191    fs.insert_tree(
16192        path!("/a"),
16193        json!({
16194            "first.rs": sample_text_1,
16195            "second.rs": sample_text_2,
16196            "third.rs": sample_text_3,
16197        }),
16198    )
16199    .await;
16200    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16201    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16202    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16203    let worktree = project.update(cx, |project, cx| {
16204        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16205        assert_eq!(worktrees.len(), 1);
16206        worktrees.pop().unwrap()
16207    });
16208    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16209
16210    let buffer_1 = project
16211        .update(cx, |project, cx| {
16212            project.open_buffer((worktree_id, "first.rs"), cx)
16213        })
16214        .await
16215        .unwrap();
16216    let buffer_2 = project
16217        .update(cx, |project, cx| {
16218            project.open_buffer((worktree_id, "second.rs"), cx)
16219        })
16220        .await
16221        .unwrap();
16222    let buffer_3 = project
16223        .update(cx, |project, cx| {
16224            project.open_buffer((worktree_id, "third.rs"), cx)
16225        })
16226        .await
16227        .unwrap();
16228
16229    let multi_buffer = cx.new(|cx| {
16230        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16231        multi_buffer.push_excerpts(
16232            buffer_1.clone(),
16233            [ExcerptRange {
16234                context: Point::new(0, 0)..Point::new(3, 0),
16235                primary: None,
16236            }],
16237            cx,
16238        );
16239        multi_buffer.push_excerpts(
16240            buffer_2.clone(),
16241            [ExcerptRange {
16242                context: Point::new(0, 0)..Point::new(3, 0),
16243                primary: None,
16244            }],
16245            cx,
16246        );
16247        multi_buffer.push_excerpts(
16248            buffer_3.clone(),
16249            [ExcerptRange {
16250                context: Point::new(0, 0)..Point::new(3, 0),
16251                primary: None,
16252            }],
16253            cx,
16254        );
16255        multi_buffer
16256    });
16257
16258    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16259        Editor::new(
16260            EditorMode::Full,
16261            multi_buffer,
16262            Some(project.clone()),
16263            true,
16264            window,
16265            cx,
16266        )
16267    });
16268
16269    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
16270    assert_eq!(
16271        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16272        full_text,
16273    );
16274
16275    multi_buffer_editor.update(cx, |editor, cx| {
16276        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16277    });
16278    assert_eq!(
16279        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16280        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
16281        "After folding the first buffer, its text should not be displayed"
16282    );
16283
16284    multi_buffer_editor.update(cx, |editor, cx| {
16285        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16286    });
16287
16288    assert_eq!(
16289        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16290        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
16291        "After folding the second buffer, its text should not be displayed"
16292    );
16293
16294    multi_buffer_editor.update(cx, |editor, cx| {
16295        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16296    });
16297    assert_eq!(
16298        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16299        "\n\n\n\n\n",
16300        "After folding the third buffer, its text should not be displayed"
16301    );
16302
16303    multi_buffer_editor.update(cx, |editor, cx| {
16304        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16305    });
16306    assert_eq!(
16307        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16308        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
16309        "After unfolding the second buffer, its text should be displayed"
16310    );
16311
16312    multi_buffer_editor.update(cx, |editor, cx| {
16313        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16314    });
16315    assert_eq!(
16316        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16317        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
16318        "After unfolding the first buffer, its text should be displayed"
16319    );
16320
16321    multi_buffer_editor.update(cx, |editor, cx| {
16322        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16323    });
16324    assert_eq!(
16325        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16326        full_text,
16327        "After unfolding all buffers, all original text should be displayed"
16328    );
16329}
16330
16331#[gpui::test]
16332async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16333    init_test(cx, |_| {});
16334
16335    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16336
16337    let fs = FakeFs::new(cx.executor());
16338    fs.insert_tree(
16339        path!("/a"),
16340        json!({
16341            "main.rs": sample_text,
16342        }),
16343    )
16344    .await;
16345    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16346    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16347    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16348    let worktree = project.update(cx, |project, cx| {
16349        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16350        assert_eq!(worktrees.len(), 1);
16351        worktrees.pop().unwrap()
16352    });
16353    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16354
16355    let buffer_1 = project
16356        .update(cx, |project, cx| {
16357            project.open_buffer((worktree_id, "main.rs"), cx)
16358        })
16359        .await
16360        .unwrap();
16361
16362    let multi_buffer = cx.new(|cx| {
16363        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16364        multi_buffer.push_excerpts(
16365            buffer_1.clone(),
16366            [ExcerptRange {
16367                context: Point::new(0, 0)
16368                    ..Point::new(
16369                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16370                        0,
16371                    ),
16372                primary: None,
16373            }],
16374            cx,
16375        );
16376        multi_buffer
16377    });
16378    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16379        Editor::new(
16380            EditorMode::Full,
16381            multi_buffer,
16382            Some(project.clone()),
16383            true,
16384            window,
16385            cx,
16386        )
16387    });
16388
16389    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16390    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16391        enum TestHighlight {}
16392        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16393        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16394        editor.highlight_text::<TestHighlight>(
16395            vec![highlight_range.clone()],
16396            HighlightStyle::color(Hsla::green()),
16397            cx,
16398        );
16399        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16400    });
16401
16402    let full_text = format!("\n\n\n{sample_text}\n");
16403    assert_eq!(
16404        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16405        full_text,
16406    );
16407}
16408
16409#[gpui::test]
16410async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16411    init_test(cx, |_| {});
16412    cx.update(|cx| {
16413        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16414            "keymaps/default-linux.json",
16415            cx,
16416        )
16417        .unwrap();
16418        cx.bind_keys(default_key_bindings);
16419    });
16420
16421    let (editor, cx) = cx.add_window_view(|window, cx| {
16422        let multi_buffer = MultiBuffer::build_multi(
16423            [
16424                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16425                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16426                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16427                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16428            ],
16429            cx,
16430        );
16431        let mut editor = Editor::new(
16432            EditorMode::Full,
16433            multi_buffer.clone(),
16434            None,
16435            true,
16436            window,
16437            cx,
16438        );
16439
16440        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16441        // fold all but the second buffer, so that we test navigating between two
16442        // adjacent folded buffers, as well as folded buffers at the start and
16443        // end the multibuffer
16444        editor.fold_buffer(buffer_ids[0], cx);
16445        editor.fold_buffer(buffer_ids[2], cx);
16446        editor.fold_buffer(buffer_ids[3], cx);
16447
16448        editor
16449    });
16450    cx.simulate_resize(size(px(1000.), px(1000.)));
16451
16452    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16453    cx.assert_excerpts_with_selections(indoc! {"
16454        [EXCERPT]
16455        ˇ[FOLDED]
16456        [EXCERPT]
16457        a1
16458        b1
16459        [EXCERPT]
16460        [FOLDED]
16461        [EXCERPT]
16462        [FOLDED]
16463        "
16464    });
16465    cx.simulate_keystroke("down");
16466    cx.assert_excerpts_with_selections(indoc! {"
16467        [EXCERPT]
16468        [FOLDED]
16469        [EXCERPT]
16470        ˇa1
16471        b1
16472        [EXCERPT]
16473        [FOLDED]
16474        [EXCERPT]
16475        [FOLDED]
16476        "
16477    });
16478    cx.simulate_keystroke("down");
16479    cx.assert_excerpts_with_selections(indoc! {"
16480        [EXCERPT]
16481        [FOLDED]
16482        [EXCERPT]
16483        a1
16484        ˇb1
16485        [EXCERPT]
16486        [FOLDED]
16487        [EXCERPT]
16488        [FOLDED]
16489        "
16490    });
16491    cx.simulate_keystroke("down");
16492    cx.assert_excerpts_with_selections(indoc! {"
16493        [EXCERPT]
16494        [FOLDED]
16495        [EXCERPT]
16496        a1
16497        b1
16498        ˇ[EXCERPT]
16499        [FOLDED]
16500        [EXCERPT]
16501        [FOLDED]
16502        "
16503    });
16504    cx.simulate_keystroke("down");
16505    cx.assert_excerpts_with_selections(indoc! {"
16506        [EXCERPT]
16507        [FOLDED]
16508        [EXCERPT]
16509        a1
16510        b1
16511        [EXCERPT]
16512        ˇ[FOLDED]
16513        [EXCERPT]
16514        [FOLDED]
16515        "
16516    });
16517    for _ in 0..5 {
16518        cx.simulate_keystroke("down");
16519        cx.assert_excerpts_with_selections(indoc! {"
16520            [EXCERPT]
16521            [FOLDED]
16522            [EXCERPT]
16523            a1
16524            b1
16525            [EXCERPT]
16526            [FOLDED]
16527            [EXCERPT]
16528            ˇ[FOLDED]
16529            "
16530        });
16531    }
16532
16533    cx.simulate_keystroke("up");
16534    cx.assert_excerpts_with_selections(indoc! {"
16535        [EXCERPT]
16536        [FOLDED]
16537        [EXCERPT]
16538        a1
16539        b1
16540        [EXCERPT]
16541        ˇ[FOLDED]
16542        [EXCERPT]
16543        [FOLDED]
16544        "
16545    });
16546    cx.simulate_keystroke("up");
16547    cx.assert_excerpts_with_selections(indoc! {"
16548        [EXCERPT]
16549        [FOLDED]
16550        [EXCERPT]
16551        a1
16552        b1
16553        ˇ[EXCERPT]
16554        [FOLDED]
16555        [EXCERPT]
16556        [FOLDED]
16557        "
16558    });
16559    cx.simulate_keystroke("up");
16560    cx.assert_excerpts_with_selections(indoc! {"
16561        [EXCERPT]
16562        [FOLDED]
16563        [EXCERPT]
16564        a1
16565        ˇb1
16566        [EXCERPT]
16567        [FOLDED]
16568        [EXCERPT]
16569        [FOLDED]
16570        "
16571    });
16572    cx.simulate_keystroke("up");
16573    cx.assert_excerpts_with_selections(indoc! {"
16574        [EXCERPT]
16575        [FOLDED]
16576        [EXCERPT]
16577        ˇa1
16578        b1
16579        [EXCERPT]
16580        [FOLDED]
16581        [EXCERPT]
16582        [FOLDED]
16583        "
16584    });
16585    for _ in 0..5 {
16586        cx.simulate_keystroke("up");
16587        cx.assert_excerpts_with_selections(indoc! {"
16588            [EXCERPT]
16589            ˇ[FOLDED]
16590            [EXCERPT]
16591            a1
16592            b1
16593            [EXCERPT]
16594            [FOLDED]
16595            [EXCERPT]
16596            [FOLDED]
16597            "
16598        });
16599    }
16600}
16601
16602#[gpui::test]
16603async fn test_inline_completion_text(cx: &mut TestAppContext) {
16604    init_test(cx, |_| {});
16605
16606    // Simple insertion
16607    assert_highlighted_edits(
16608        "Hello, world!",
16609        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16610        true,
16611        cx,
16612        |highlighted_edits, cx| {
16613            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16614            assert_eq!(highlighted_edits.highlights.len(), 1);
16615            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16616            assert_eq!(
16617                highlighted_edits.highlights[0].1.background_color,
16618                Some(cx.theme().status().created_background)
16619            );
16620        },
16621    )
16622    .await;
16623
16624    // Replacement
16625    assert_highlighted_edits(
16626        "This is a test.",
16627        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16628        false,
16629        cx,
16630        |highlighted_edits, cx| {
16631            assert_eq!(highlighted_edits.text, "That is a test.");
16632            assert_eq!(highlighted_edits.highlights.len(), 1);
16633            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16634            assert_eq!(
16635                highlighted_edits.highlights[0].1.background_color,
16636                Some(cx.theme().status().created_background)
16637            );
16638        },
16639    )
16640    .await;
16641
16642    // Multiple edits
16643    assert_highlighted_edits(
16644        "Hello, world!",
16645        vec![
16646            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16647            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16648        ],
16649        false,
16650        cx,
16651        |highlighted_edits, cx| {
16652            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16653            assert_eq!(highlighted_edits.highlights.len(), 2);
16654            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16655            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16656            assert_eq!(
16657                highlighted_edits.highlights[0].1.background_color,
16658                Some(cx.theme().status().created_background)
16659            );
16660            assert_eq!(
16661                highlighted_edits.highlights[1].1.background_color,
16662                Some(cx.theme().status().created_background)
16663            );
16664        },
16665    )
16666    .await;
16667
16668    // Multiple lines with edits
16669    assert_highlighted_edits(
16670        "First line\nSecond line\nThird line\nFourth line",
16671        vec![
16672            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16673            (
16674                Point::new(2, 0)..Point::new(2, 10),
16675                "New third line".to_string(),
16676            ),
16677            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16678        ],
16679        false,
16680        cx,
16681        |highlighted_edits, cx| {
16682            assert_eq!(
16683                highlighted_edits.text,
16684                "Second modified\nNew third line\nFourth updated line"
16685            );
16686            assert_eq!(highlighted_edits.highlights.len(), 3);
16687            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16688            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16689            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16690            for highlight in &highlighted_edits.highlights {
16691                assert_eq!(
16692                    highlight.1.background_color,
16693                    Some(cx.theme().status().created_background)
16694                );
16695            }
16696        },
16697    )
16698    .await;
16699}
16700
16701#[gpui::test]
16702async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16703    init_test(cx, |_| {});
16704
16705    // Deletion
16706    assert_highlighted_edits(
16707        "Hello, world!",
16708        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16709        true,
16710        cx,
16711        |highlighted_edits, cx| {
16712            assert_eq!(highlighted_edits.text, "Hello, world!");
16713            assert_eq!(highlighted_edits.highlights.len(), 1);
16714            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16715            assert_eq!(
16716                highlighted_edits.highlights[0].1.background_color,
16717                Some(cx.theme().status().deleted_background)
16718            );
16719        },
16720    )
16721    .await;
16722
16723    // Insertion
16724    assert_highlighted_edits(
16725        "Hello, world!",
16726        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16727        true,
16728        cx,
16729        |highlighted_edits, cx| {
16730            assert_eq!(highlighted_edits.highlights.len(), 1);
16731            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16732            assert_eq!(
16733                highlighted_edits.highlights[0].1.background_color,
16734                Some(cx.theme().status().created_background)
16735            );
16736        },
16737    )
16738    .await;
16739}
16740
16741async fn assert_highlighted_edits(
16742    text: &str,
16743    edits: Vec<(Range<Point>, String)>,
16744    include_deletions: bool,
16745    cx: &mut TestAppContext,
16746    assertion_fn: impl Fn(HighlightedText, &App),
16747) {
16748    let window = cx.add_window(|window, cx| {
16749        let buffer = MultiBuffer::build_simple(text, cx);
16750        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16751    });
16752    let cx = &mut VisualTestContext::from_window(*window, cx);
16753
16754    let (buffer, snapshot) = window
16755        .update(cx, |editor, _window, cx| {
16756            (
16757                editor.buffer().clone(),
16758                editor.buffer().read(cx).snapshot(cx),
16759            )
16760        })
16761        .unwrap();
16762
16763    let edits = edits
16764        .into_iter()
16765        .map(|(range, edit)| {
16766            (
16767                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16768                edit,
16769            )
16770        })
16771        .collect::<Vec<_>>();
16772
16773    let text_anchor_edits = edits
16774        .clone()
16775        .into_iter()
16776        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16777        .collect::<Vec<_>>();
16778
16779    let edit_preview = window
16780        .update(cx, |_, _window, cx| {
16781            buffer
16782                .read(cx)
16783                .as_singleton()
16784                .unwrap()
16785                .read(cx)
16786                .preview_edits(text_anchor_edits.into(), cx)
16787        })
16788        .unwrap()
16789        .await;
16790
16791    cx.update(|_window, cx| {
16792        let highlighted_edits = inline_completion_edit_text(
16793            &snapshot.as_singleton().unwrap().2,
16794            &edits,
16795            &edit_preview,
16796            include_deletions,
16797            cx,
16798        );
16799        assertion_fn(highlighted_edits, cx)
16800    });
16801}
16802
16803#[gpui::test]
16804async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16805    init_test(cx, |_| {});
16806    let capabilities = lsp::ServerCapabilities {
16807        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16808            prepare_provider: Some(true),
16809            work_done_progress_options: Default::default(),
16810        })),
16811        ..Default::default()
16812    };
16813    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16814
16815    cx.set_state(indoc! {"
16816        struct Fˇoo {}
16817    "});
16818
16819    cx.update_editor(|editor, _, cx| {
16820        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16821        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16822        editor.highlight_background::<DocumentHighlightRead>(
16823            &[highlight_range],
16824            |c| c.editor_document_highlight_read_background,
16825            cx,
16826        );
16827    });
16828
16829    let mut prepare_rename_handler =
16830        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16831            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16832                start: lsp::Position {
16833                    line: 0,
16834                    character: 7,
16835                },
16836                end: lsp::Position {
16837                    line: 0,
16838                    character: 10,
16839                },
16840            })))
16841        });
16842    let prepare_rename_task = cx
16843        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16844        .expect("Prepare rename was not started");
16845    prepare_rename_handler.next().await.unwrap();
16846    prepare_rename_task.await.expect("Prepare rename failed");
16847
16848    let mut rename_handler =
16849        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16850            let edit = lsp::TextEdit {
16851                range: lsp::Range {
16852                    start: lsp::Position {
16853                        line: 0,
16854                        character: 7,
16855                    },
16856                    end: lsp::Position {
16857                        line: 0,
16858                        character: 10,
16859                    },
16860                },
16861                new_text: "FooRenamed".to_string(),
16862            };
16863            Ok(Some(lsp::WorkspaceEdit::new(
16864                // Specify the same edit twice
16865                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16866            )))
16867        });
16868    let rename_task = cx
16869        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16870        .expect("Confirm rename was not started");
16871    rename_handler.next().await.unwrap();
16872    rename_task.await.expect("Confirm rename failed");
16873    cx.run_until_parked();
16874
16875    // Despite two edits, only one is actually applied as those are identical
16876    cx.assert_editor_state(indoc! {"
16877        struct FooRenamedˇ {}
16878    "});
16879}
16880
16881#[gpui::test]
16882async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16883    init_test(cx, |_| {});
16884    // These capabilities indicate that the server does not support prepare rename.
16885    let capabilities = lsp::ServerCapabilities {
16886        rename_provider: Some(lsp::OneOf::Left(true)),
16887        ..Default::default()
16888    };
16889    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16890
16891    cx.set_state(indoc! {"
16892        struct Fˇoo {}
16893    "});
16894
16895    cx.update_editor(|editor, _window, cx| {
16896        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16897        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16898        editor.highlight_background::<DocumentHighlightRead>(
16899            &[highlight_range],
16900            |c| c.editor_document_highlight_read_background,
16901            cx,
16902        );
16903    });
16904
16905    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16906        .expect("Prepare rename was not started")
16907        .await
16908        .expect("Prepare rename failed");
16909
16910    let mut rename_handler =
16911        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16912            let edit = lsp::TextEdit {
16913                range: lsp::Range {
16914                    start: lsp::Position {
16915                        line: 0,
16916                        character: 7,
16917                    },
16918                    end: lsp::Position {
16919                        line: 0,
16920                        character: 10,
16921                    },
16922                },
16923                new_text: "FooRenamed".to_string(),
16924            };
16925            Ok(Some(lsp::WorkspaceEdit::new(
16926                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16927            )))
16928        });
16929    let rename_task = cx
16930        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16931        .expect("Confirm rename was not started");
16932    rename_handler.next().await.unwrap();
16933    rename_task.await.expect("Confirm rename failed");
16934    cx.run_until_parked();
16935
16936    // Correct range is renamed, as `surrounding_word` is used to find it.
16937    cx.assert_editor_state(indoc! {"
16938        struct FooRenamedˇ {}
16939    "});
16940}
16941
16942#[gpui::test]
16943async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16944    init_test(cx, |_| {});
16945    let mut cx = EditorTestContext::new(cx).await;
16946
16947    let language = Arc::new(
16948        Language::new(
16949            LanguageConfig::default(),
16950            Some(tree_sitter_html::LANGUAGE.into()),
16951        )
16952        .with_brackets_query(
16953            r#"
16954            ("<" @open "/>" @close)
16955            ("</" @open ">" @close)
16956            ("<" @open ">" @close)
16957            ("\"" @open "\"" @close)
16958            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16959        "#,
16960        )
16961        .unwrap(),
16962    );
16963    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16964
16965    cx.set_state(indoc! {"
16966        <span>ˇ</span>
16967    "});
16968    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16969    cx.assert_editor_state(indoc! {"
16970        <span>
16971        ˇ
16972        </span>
16973    "});
16974
16975    cx.set_state(indoc! {"
16976        <span><span></span>ˇ</span>
16977    "});
16978    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16979    cx.assert_editor_state(indoc! {"
16980        <span><span></span>
16981        ˇ</span>
16982    "});
16983
16984    cx.set_state(indoc! {"
16985        <span>ˇ
16986        </span>
16987    "});
16988    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16989    cx.assert_editor_state(indoc! {"
16990        <span>
16991        ˇ
16992        </span>
16993    "});
16994}
16995
16996mod autoclose_tags {
16997    use super::*;
16998    use language::language_settings::JsxTagAutoCloseSettings;
16999    use languages::language;
17000
17001    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17002        init_test(cx, |settings| {
17003            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17004        });
17005
17006        let mut cx = EditorTestContext::new(cx).await;
17007        cx.update_buffer(|buffer, cx| {
17008            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17009
17010            buffer.set_language(Some(language), cx)
17011        });
17012
17013        cx
17014    }
17015
17016    macro_rules! check {
17017        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17018            #[gpui::test]
17019            async fn $name(cx: &mut TestAppContext) {
17020                let mut cx = test_setup(cx).await;
17021                cx.set_state($initial);
17022                cx.run_until_parked();
17023
17024                cx.update_editor(|editor, window, cx| {
17025                    editor.handle_input($input, window, cx);
17026                });
17027                cx.run_until_parked();
17028                cx.assert_editor_state($expected);
17029            }
17030        };
17031    }
17032
17033    check!(
17034        test_basic,
17035        "<divˇ" + ">" => "<div>ˇ</div>"
17036    );
17037
17038    check!(
17039        test_basic_nested,
17040        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17041    );
17042
17043    check!(
17044        test_basic_ignore_already_closed,
17045        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17046    );
17047
17048    check!(
17049        test_doesnt_autoclose_closing_tag,
17050        "</divˇ" + ">" => "</div>ˇ"
17051    );
17052
17053    check!(
17054        test_jsx_attr,
17055        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17056    );
17057
17058    check!(
17059        test_ignores_closing_tags_in_expr_block,
17060        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17061    );
17062
17063    check!(
17064        test_doesnt_autoclose_on_gt_in_expr,
17065        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17066    );
17067
17068    check!(
17069        test_ignores_closing_tags_with_different_tag_names,
17070        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17071    );
17072
17073    check!(
17074        test_autocloses_in_jsx_expression,
17075        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17076    );
17077
17078    check!(
17079        test_doesnt_autoclose_already_closed_in_jsx_expression,
17080        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17081    );
17082
17083    check!(
17084        test_autocloses_fragment,
17085        "" + ">" => "<>ˇ</>"
17086    );
17087
17088    check!(
17089        test_does_not_include_type_argument_in_autoclose_tag_name,
17090        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17091    );
17092
17093    check!(
17094        test_does_not_autoclose_doctype,
17095        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17096    );
17097
17098    check!(
17099        test_does_not_autoclose_comment,
17100        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17101    );
17102
17103    check!(
17104        test_multi_cursor_autoclose_same_tag,
17105        r#"
17106        <divˇ
17107        <divˇ
17108        "#
17109        + ">" =>
17110        r#"
17111        <div>ˇ</div>
17112        <div>ˇ</div>
17113        "#
17114    );
17115
17116    check!(
17117        test_multi_cursor_autoclose_different_tags,
17118        r#"
17119        <divˇ
17120        <spanˇ
17121        "#
17122        + ">" =>
17123        r#"
17124        <div>ˇ</div>
17125        <span>ˇ</span>
17126        "#
17127    );
17128
17129    check!(
17130        test_multi_cursor_autoclose_some_dont_autoclose_others,
17131        r#"
17132        <divˇ
17133        <div /ˇ
17134        <spanˇ</span>
17135        <!DOCTYPE htmlˇ
17136        </headˇ
17137        <Component<T>ˇ
17138        ˇ
17139        "#
17140        + ">" =>
17141        r#"
17142        <div>ˇ</div>
17143        <div />ˇ
17144        <span>ˇ</span>
17145        <!DOCTYPE html>ˇ
17146        </head>ˇ
17147        <Component<T>>ˇ</Component>
1714817149        "#
17150    );
17151
17152    check!(
17153        test_doesnt_mess_up_trailing_text,
17154        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17155    );
17156
17157    #[gpui::test]
17158    async fn test_multibuffer(cx: &mut TestAppContext) {
17159        init_test(cx, |settings| {
17160            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17161        });
17162
17163        let buffer_a = cx.new(|cx| {
17164            let mut buf = language::Buffer::local("<div", cx);
17165            buf.set_language(
17166                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17167                cx,
17168            );
17169            buf
17170        });
17171        let buffer_b = cx.new(|cx| {
17172            let mut buf = language::Buffer::local("<pre", cx);
17173            buf.set_language(
17174                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17175                cx,
17176            );
17177            buf
17178        });
17179        let buffer_c = cx.new(|cx| {
17180            let buf = language::Buffer::local("<span", cx);
17181            buf
17182        });
17183        let buffer = cx.new(|cx| {
17184            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17185            buf.push_excerpts(
17186                buffer_a,
17187                [ExcerptRange {
17188                    context: text::Anchor::MIN..text::Anchor::MAX,
17189                    primary: None,
17190                }],
17191                cx,
17192            );
17193            buf.push_excerpts(
17194                buffer_b,
17195                [ExcerptRange {
17196                    context: text::Anchor::MIN..text::Anchor::MAX,
17197                    primary: None,
17198                }],
17199                cx,
17200            );
17201            buf.push_excerpts(
17202                buffer_c,
17203                [ExcerptRange {
17204                    context: text::Anchor::MIN..text::Anchor::MAX,
17205                    primary: None,
17206                }],
17207                cx,
17208            );
17209            buf
17210        });
17211        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17212
17213        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17214
17215        cx.update_editor(|editor, window, cx| {
17216            editor.change_selections(None, window, cx, |selections| {
17217                selections.select(vec![
17218                    Selection::from_offset(4),
17219                    Selection::from_offset(9),
17220                    Selection::from_offset(15),
17221                ])
17222            })
17223        });
17224        cx.run_until_parked();
17225
17226        cx.update_editor(|editor, window, cx| {
17227            editor.handle_input(">", window, cx);
17228        });
17229        cx.run_until_parked();
17230
17231        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17232    }
17233}
17234
17235fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17236    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17237    point..point
17238}
17239
17240fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17241    let (text, ranges) = marked_text_ranges(marked_text, true);
17242    assert_eq!(editor.text(cx), text);
17243    assert_eq!(
17244        editor.selections.ranges(cx),
17245        ranges,
17246        "Assert selections are {}",
17247        marked_text
17248    );
17249}
17250
17251pub fn handle_signature_help_request(
17252    cx: &mut EditorLspTestContext,
17253    mocked_response: lsp::SignatureHelp,
17254) -> impl Future<Output = ()> {
17255    let mut request =
17256        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17257            let mocked_response = mocked_response.clone();
17258            async move { Ok(Some(mocked_response)) }
17259        });
17260
17261    async move {
17262        request.next().await;
17263    }
17264}
17265
17266/// Handle completion request passing a marked string specifying where the completion
17267/// should be triggered from using '|' character, what range should be replaced, and what completions
17268/// should be returned using '<' and '>' to delimit the range
17269pub fn handle_completion_request(
17270    cx: &mut EditorLspTestContext,
17271    marked_string: &str,
17272    completions: Vec<&'static str>,
17273    counter: Arc<AtomicUsize>,
17274) -> impl Future<Output = ()> {
17275    let complete_from_marker: TextRangeMarker = '|'.into();
17276    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17277    let (_, mut marked_ranges) = marked_text_ranges_by(
17278        marked_string,
17279        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17280    );
17281
17282    let complete_from_position =
17283        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17284    let replace_range =
17285        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17286
17287    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17288        let completions = completions.clone();
17289        counter.fetch_add(1, atomic::Ordering::Release);
17290        async move {
17291            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17292            assert_eq!(
17293                params.text_document_position.position,
17294                complete_from_position
17295            );
17296            Ok(Some(lsp::CompletionResponse::Array(
17297                completions
17298                    .iter()
17299                    .map(|completion_text| lsp::CompletionItem {
17300                        label: completion_text.to_string(),
17301                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17302                            range: replace_range,
17303                            new_text: completion_text.to_string(),
17304                        })),
17305                        ..Default::default()
17306                    })
17307                    .collect(),
17308            )))
17309        }
17310    });
17311
17312    async move {
17313        request.next().await;
17314    }
17315}
17316
17317fn handle_resolve_completion_request(
17318    cx: &mut EditorLspTestContext,
17319    edits: Option<Vec<(&'static str, &'static str)>>,
17320) -> impl Future<Output = ()> {
17321    let edits = edits.map(|edits| {
17322        edits
17323            .iter()
17324            .map(|(marked_string, new_text)| {
17325                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17326                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17327                lsp::TextEdit::new(replace_range, new_text.to_string())
17328            })
17329            .collect::<Vec<_>>()
17330    });
17331
17332    let mut request =
17333        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17334            let edits = edits.clone();
17335            async move {
17336                Ok(lsp::CompletionItem {
17337                    additional_text_edits: edits,
17338                    ..Default::default()
17339                })
17340            }
17341        });
17342
17343    async move {
17344        request.next().await;
17345    }
17346}
17347
17348pub(crate) fn update_test_language_settings(
17349    cx: &mut TestAppContext,
17350    f: impl Fn(&mut AllLanguageSettingsContent),
17351) {
17352    cx.update(|cx| {
17353        SettingsStore::update_global(cx, |store, cx| {
17354            store.update_user_settings::<AllLanguageSettings>(cx, f);
17355        });
17356    });
17357}
17358
17359pub(crate) fn update_test_project_settings(
17360    cx: &mut TestAppContext,
17361    f: impl Fn(&mut ProjectSettings),
17362) {
17363    cx.update(|cx| {
17364        SettingsStore::update_global(cx, |store, cx| {
17365            store.update_user_settings::<ProjectSettings>(cx, f);
17366        });
17367    });
17368}
17369
17370pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17371    cx.update(|cx| {
17372        assets::Assets.load_test_fonts(cx);
17373        let store = SettingsStore::test(cx);
17374        cx.set_global(store);
17375        theme::init(theme::LoadThemes::JustBase, cx);
17376        release_channel::init(SemanticVersion::default(), cx);
17377        client::init_settings(cx);
17378        language::init(cx);
17379        Project::init_settings(cx);
17380        workspace::init_settings(cx);
17381        crate::init(cx);
17382    });
17383
17384    update_test_language_settings(cx, f);
17385}
17386
17387#[track_caller]
17388fn assert_hunk_revert(
17389    not_reverted_text_with_selections: &str,
17390    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17391    expected_reverted_text_with_selections: &str,
17392    base_text: &str,
17393    cx: &mut EditorLspTestContext,
17394) {
17395    cx.set_state(not_reverted_text_with_selections);
17396    cx.set_head_text(base_text);
17397    cx.executor().run_until_parked();
17398
17399    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17400        let snapshot = editor.snapshot(window, cx);
17401        let reverted_hunk_statuses = snapshot
17402            .buffer_snapshot
17403            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17404            .map(|hunk| hunk.status().kind)
17405            .collect::<Vec<_>>();
17406
17407        editor.git_restore(&Default::default(), window, cx);
17408        reverted_hunk_statuses
17409    });
17410    cx.executor().run_until_parked();
17411    cx.assert_editor_state(expected_reverted_text_with_selections);
17412    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17413}