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