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