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 move_to_end = MoveToEndOfLine {
 1518        stop_at_soft_wraps: true,
 1519    };
 1520
 1521    let editor = cx.add_window(|window, cx| {
 1522        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1523        build_editor(buffer, window, cx)
 1524    });
 1525    _ = editor.update(cx, |editor, window, cx| {
 1526        editor.change_selections(None, window, cx, |s| {
 1527            s.select_display_ranges([
 1528                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1529                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1530            ]);
 1531        });
 1532    });
 1533
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1536        assert_eq!(
 1537            editor.selections.display_ranges(cx),
 1538            &[
 1539                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1540                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1541            ]
 1542        );
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_end_of_line(&move_to_end, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1573                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1574            ]
 1575        );
 1576    });
 1577
 1578    // Moving to the end of line again is a no-op.
 1579    _ = editor.update(cx, |editor, window, cx| {
 1580        editor.move_to_end_of_line(&move_to_end, window, cx);
 1581        assert_eq!(
 1582            editor.selections.display_ranges(cx),
 1583            &[
 1584                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1585                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1586            ]
 1587        );
 1588    });
 1589
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_left(&MoveLeft, window, cx);
 1592        editor.select_to_beginning_of_line(
 1593            &SelectToBeginningOfLine {
 1594                stop_at_soft_wraps: true,
 1595                stop_at_indent: true,
 1596            },
 1597            window,
 1598            cx,
 1599        );
 1600        assert_eq!(
 1601            editor.selections.display_ranges(cx),
 1602            &[
 1603                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1604                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1605            ]
 1606        );
 1607    });
 1608
 1609    _ = editor.update(cx, |editor, window, cx| {
 1610        editor.select_to_beginning_of_line(
 1611            &SelectToBeginningOfLine {
 1612                stop_at_soft_wraps: true,
 1613                stop_at_indent: true,
 1614            },
 1615            window,
 1616            cx,
 1617        );
 1618        assert_eq!(
 1619            editor.selections.display_ranges(cx),
 1620            &[
 1621                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1622                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1623            ]
 1624        );
 1625    });
 1626
 1627    _ = editor.update(cx, |editor, window, cx| {
 1628        editor.select_to_beginning_of_line(
 1629            &SelectToBeginningOfLine {
 1630                stop_at_soft_wraps: true,
 1631                stop_at_indent: true,
 1632            },
 1633            window,
 1634            cx,
 1635        );
 1636        assert_eq!(
 1637            editor.selections.display_ranges(cx),
 1638            &[
 1639                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1640                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1641            ]
 1642        );
 1643    });
 1644
 1645    _ = editor.update(cx, |editor, window, cx| {
 1646        editor.select_to_end_of_line(
 1647            &SelectToEndOfLine {
 1648                stop_at_soft_wraps: true,
 1649            },
 1650            window,
 1651            cx,
 1652        );
 1653        assert_eq!(
 1654            editor.selections.display_ranges(cx),
 1655            &[
 1656                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1657                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1658            ]
 1659        );
 1660    });
 1661
 1662    _ = editor.update(cx, |editor, window, cx| {
 1663        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1664        assert_eq!(editor.display_text(cx), "ab\n  de");
 1665        assert_eq!(
 1666            editor.selections.display_ranges(cx),
 1667            &[
 1668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1669                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1670            ]
 1671        );
 1672    });
 1673
 1674    _ = editor.update(cx, |editor, window, cx| {
 1675        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1676        assert_eq!(editor.display_text(cx), "\n");
 1677        assert_eq!(
 1678            editor.selections.display_ranges(cx),
 1679            &[
 1680                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1681                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1682            ]
 1683        );
 1684    });
 1685}
 1686
 1687#[gpui::test]
 1688fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1689    init_test(cx, |_| {});
 1690    let move_to_beg = MoveToBeginningOfLine {
 1691        stop_at_soft_wraps: false,
 1692        stop_at_indent: false,
 1693    };
 1694
 1695    let move_to_end = MoveToEndOfLine {
 1696        stop_at_soft_wraps: false,
 1697    };
 1698
 1699    let editor = cx.add_window(|window, cx| {
 1700        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1701        build_editor(buffer, window, cx)
 1702    });
 1703
 1704    _ = editor.update(cx, |editor, window, cx| {
 1705        editor.set_wrap_width(Some(140.0.into()), cx);
 1706
 1707        // We expect the following lines after wrapping
 1708        // ```
 1709        // thequickbrownfox
 1710        // jumpedoverthelazydo
 1711        // gs
 1712        // ```
 1713        // The final `gs` was soft-wrapped onto a new line.
 1714        assert_eq!(
 1715            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1716            editor.display_text(cx),
 1717        );
 1718
 1719        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1720        // Start the cursor at the `k` on the first line
 1721        editor.change_selections(None, window, cx, |s| {
 1722            s.select_display_ranges([
 1723                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1724            ]);
 1725        });
 1726
 1727        // Moving to the beginning of the line should put us at the beginning of the line.
 1728        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1729        assert_eq!(
 1730            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1731            editor.selections.display_ranges(cx)
 1732        );
 1733
 1734        // Moving to the end of the line should put us at the end of the line.
 1735        editor.move_to_end_of_line(&move_to_end, window, cx);
 1736        assert_eq!(
 1737            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1738            editor.selections.display_ranges(cx)
 1739        );
 1740
 1741        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1742        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1743        editor.change_selections(None, window, cx, |s| {
 1744            s.select_display_ranges([
 1745                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1746            ]);
 1747        });
 1748
 1749        // Moving to the beginning of the line should put us at the start of the second line of
 1750        // display text, i.e., the `j`.
 1751        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1752        assert_eq!(
 1753            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1754            editor.selections.display_ranges(cx)
 1755        );
 1756
 1757        // Moving to the beginning of the line again should be a no-op.
 1758        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1759        assert_eq!(
 1760            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1761            editor.selections.display_ranges(cx)
 1762        );
 1763
 1764        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1765        // next display line.
 1766        editor.move_to_end_of_line(&move_to_end, window, cx);
 1767        assert_eq!(
 1768            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1769            editor.selections.display_ranges(cx)
 1770        );
 1771
 1772        // Moving to the end of the line again should be a no-op.
 1773        editor.move_to_end_of_line(&move_to_end, window, cx);
 1774        assert_eq!(
 1775            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1776            editor.selections.display_ranges(cx)
 1777        );
 1778    });
 1779}
 1780
 1781#[gpui::test]
 1782fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1783    init_test(cx, |_| {});
 1784
 1785    let editor = cx.add_window(|window, cx| {
 1786        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1787        build_editor(buffer, window, cx)
 1788    });
 1789    _ = editor.update(cx, |editor, window, cx| {
 1790        editor.change_selections(None, window, cx, |s| {
 1791            s.select_display_ranges([
 1792                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1793                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1794            ])
 1795        });
 1796
 1797        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1798        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1799
 1800        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1801        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1802
 1803        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1804        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1805
 1806        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1807        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1808
 1809        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1810        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1811
 1812        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1813        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1814
 1815        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1816        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1817
 1818        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1819        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1820
 1821        editor.move_right(&MoveRight, window, cx);
 1822        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1823        assert_selection_ranges(
 1824            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1825            editor,
 1826            cx,
 1827        );
 1828
 1829        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1830        assert_selection_ranges(
 1831            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1832            editor,
 1833            cx,
 1834        );
 1835
 1836        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1837        assert_selection_ranges(
 1838            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1839            editor,
 1840            cx,
 1841        );
 1842    });
 1843}
 1844
 1845#[gpui::test]
 1846fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1847    init_test(cx, |_| {});
 1848
 1849    let editor = cx.add_window(|window, cx| {
 1850        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1851        build_editor(buffer, window, cx)
 1852    });
 1853
 1854    _ = editor.update(cx, |editor, window, cx| {
 1855        editor.set_wrap_width(Some(140.0.into()), cx);
 1856        assert_eq!(
 1857            editor.display_text(cx),
 1858            "use one::{\n    two::three::\n    four::five\n};"
 1859        );
 1860
 1861        editor.change_selections(None, window, cx, |s| {
 1862            s.select_display_ranges([
 1863                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1864            ]);
 1865        });
 1866
 1867        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1868        assert_eq!(
 1869            editor.selections.display_ranges(cx),
 1870            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1871        );
 1872
 1873        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1877        );
 1878
 1879        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1880        assert_eq!(
 1881            editor.selections.display_ranges(cx),
 1882            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1883        );
 1884
 1885        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1886        assert_eq!(
 1887            editor.selections.display_ranges(cx),
 1888            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1889        );
 1890
 1891        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1892        assert_eq!(
 1893            editor.selections.display_ranges(cx),
 1894            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1895        );
 1896
 1897        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1898        assert_eq!(
 1899            editor.selections.display_ranges(cx),
 1900            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1901        );
 1902    });
 1903}
 1904
 1905#[gpui::test]
 1906async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 1907    init_test(cx, |_| {});
 1908    let mut cx = EditorTestContext::new(cx).await;
 1909
 1910    let line_height = cx.editor(|editor, window, _| {
 1911        editor
 1912            .style()
 1913            .unwrap()
 1914            .text
 1915            .line_height_in_pixels(window.rem_size())
 1916    });
 1917    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1918
 1919    cx.set_state(
 1920        &r#"ˇone
 1921        two
 1922
 1923        three
 1924        fourˇ
 1925        five
 1926
 1927        six"#
 1928            .unindent(),
 1929    );
 1930
 1931    cx.update_editor(|editor, window, cx| {
 1932        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1933    });
 1934    cx.assert_editor_state(
 1935        &r#"one
 1936        two
 1937        ˇ
 1938        three
 1939        four
 1940        five
 1941        ˇ
 1942        six"#
 1943            .unindent(),
 1944    );
 1945
 1946    cx.update_editor(|editor, window, cx| {
 1947        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1948    });
 1949    cx.assert_editor_state(
 1950        &r#"one
 1951        two
 1952
 1953        three
 1954        four
 1955        five
 1956        ˇ
 1957        sixˇ"#
 1958            .unindent(),
 1959    );
 1960
 1961    cx.update_editor(|editor, window, cx| {
 1962        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1963    });
 1964    cx.assert_editor_state(
 1965        &r#"one
 1966        two
 1967
 1968        three
 1969        four
 1970        five
 1971
 1972        sixˇ"#
 1973            .unindent(),
 1974    );
 1975
 1976    cx.update_editor(|editor, window, cx| {
 1977        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1978    });
 1979    cx.assert_editor_state(
 1980        &r#"one
 1981        two
 1982
 1983        three
 1984        four
 1985        five
 1986        ˇ
 1987        six"#
 1988            .unindent(),
 1989    );
 1990
 1991    cx.update_editor(|editor, window, cx| {
 1992        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1993    });
 1994    cx.assert_editor_state(
 1995        &r#"one
 1996        two
 1997        ˇ
 1998        three
 1999        four
 2000        five
 2001
 2002        six"#
 2003            .unindent(),
 2004    );
 2005
 2006    cx.update_editor(|editor, window, cx| {
 2007        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2008    });
 2009    cx.assert_editor_state(
 2010        &r#"ˇone
 2011        two
 2012
 2013        three
 2014        four
 2015        five
 2016
 2017        six"#
 2018            .unindent(),
 2019    );
 2020}
 2021
 2022#[gpui::test]
 2023async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2024    init_test(cx, |_| {});
 2025    let mut cx = EditorTestContext::new(cx).await;
 2026    let line_height = cx.editor(|editor, window, _| {
 2027        editor
 2028            .style()
 2029            .unwrap()
 2030            .text
 2031            .line_height_in_pixels(window.rem_size())
 2032    });
 2033    let window = cx.window;
 2034    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2035
 2036    cx.set_state(
 2037        r#"ˇone
 2038        two
 2039        three
 2040        four
 2041        five
 2042        six
 2043        seven
 2044        eight
 2045        nine
 2046        ten
 2047        "#,
 2048    );
 2049
 2050    cx.update_editor(|editor, window, cx| {
 2051        assert_eq!(
 2052            editor.snapshot(window, cx).scroll_position(),
 2053            gpui::Point::new(0., 0.)
 2054        );
 2055        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2056        assert_eq!(
 2057            editor.snapshot(window, cx).scroll_position(),
 2058            gpui::Point::new(0., 3.)
 2059        );
 2060        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2061        assert_eq!(
 2062            editor.snapshot(window, cx).scroll_position(),
 2063            gpui::Point::new(0., 6.)
 2064        );
 2065        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2066        assert_eq!(
 2067            editor.snapshot(window, cx).scroll_position(),
 2068            gpui::Point::new(0., 3.)
 2069        );
 2070
 2071        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2072        assert_eq!(
 2073            editor.snapshot(window, cx).scroll_position(),
 2074            gpui::Point::new(0., 1.)
 2075        );
 2076        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2077        assert_eq!(
 2078            editor.snapshot(window, cx).scroll_position(),
 2079            gpui::Point::new(0., 3.)
 2080        );
 2081    });
 2082}
 2083
 2084#[gpui::test]
 2085async fn test_autoscroll(cx: &mut TestAppContext) {
 2086    init_test(cx, |_| {});
 2087    let mut cx = EditorTestContext::new(cx).await;
 2088
 2089    let line_height = cx.update_editor(|editor, window, cx| {
 2090        editor.set_vertical_scroll_margin(2, cx);
 2091        editor
 2092            .style()
 2093            .unwrap()
 2094            .text
 2095            .line_height_in_pixels(window.rem_size())
 2096    });
 2097    let window = cx.window;
 2098    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2099
 2100    cx.set_state(
 2101        r#"ˇone
 2102            two
 2103            three
 2104            four
 2105            five
 2106            six
 2107            seven
 2108            eight
 2109            nine
 2110            ten
 2111        "#,
 2112    );
 2113    cx.update_editor(|editor, window, cx| {
 2114        assert_eq!(
 2115            editor.snapshot(window, cx).scroll_position(),
 2116            gpui::Point::new(0., 0.0)
 2117        );
 2118    });
 2119
 2120    // Add a cursor below the visible area. Since both cursors cannot fit
 2121    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2122    // allows the vertical scroll margin below that cursor.
 2123    cx.update_editor(|editor, window, cx| {
 2124        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2125            selections.select_ranges([
 2126                Point::new(0, 0)..Point::new(0, 0),
 2127                Point::new(6, 0)..Point::new(6, 0),
 2128            ]);
 2129        })
 2130    });
 2131    cx.update_editor(|editor, window, cx| {
 2132        assert_eq!(
 2133            editor.snapshot(window, cx).scroll_position(),
 2134            gpui::Point::new(0., 3.0)
 2135        );
 2136    });
 2137
 2138    // Move down. The editor cursor scrolls down to track the newest cursor.
 2139    cx.update_editor(|editor, window, cx| {
 2140        editor.move_down(&Default::default(), window, cx);
 2141    });
 2142    cx.update_editor(|editor, window, cx| {
 2143        assert_eq!(
 2144            editor.snapshot(window, cx).scroll_position(),
 2145            gpui::Point::new(0., 4.0)
 2146        );
 2147    });
 2148
 2149    // Add a cursor above the visible area. Since both cursors fit on screen,
 2150    // the editor scrolls to show both.
 2151    cx.update_editor(|editor, window, cx| {
 2152        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2153            selections.select_ranges([
 2154                Point::new(1, 0)..Point::new(1, 0),
 2155                Point::new(6, 0)..Point::new(6, 0),
 2156            ]);
 2157        })
 2158    });
 2159    cx.update_editor(|editor, window, cx| {
 2160        assert_eq!(
 2161            editor.snapshot(window, cx).scroll_position(),
 2162            gpui::Point::new(0., 1.0)
 2163        );
 2164    });
 2165}
 2166
 2167#[gpui::test]
 2168async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2169    init_test(cx, |_| {});
 2170    let mut cx = EditorTestContext::new(cx).await;
 2171
 2172    let line_height = cx.editor(|editor, window, _cx| {
 2173        editor
 2174            .style()
 2175            .unwrap()
 2176            .text
 2177            .line_height_in_pixels(window.rem_size())
 2178    });
 2179    let window = cx.window;
 2180    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2181    cx.set_state(
 2182        &r#"
 2183        ˇone
 2184        two
 2185        threeˇ
 2186        four
 2187        five
 2188        six
 2189        seven
 2190        eight
 2191        nine
 2192        ten
 2193        "#
 2194        .unindent(),
 2195    );
 2196
 2197    cx.update_editor(|editor, window, cx| {
 2198        editor.move_page_down(&MovePageDown::default(), window, cx)
 2199    });
 2200    cx.assert_editor_state(
 2201        &r#"
 2202        one
 2203        two
 2204        three
 2205        ˇfour
 2206        five
 2207        sixˇ
 2208        seven
 2209        eight
 2210        nine
 2211        ten
 2212        "#
 2213        .unindent(),
 2214    );
 2215
 2216    cx.update_editor(|editor, window, cx| {
 2217        editor.move_page_down(&MovePageDown::default(), window, cx)
 2218    });
 2219    cx.assert_editor_state(
 2220        &r#"
 2221        one
 2222        two
 2223        three
 2224        four
 2225        five
 2226        six
 2227        ˇseven
 2228        eight
 2229        nineˇ
 2230        ten
 2231        "#
 2232        .unindent(),
 2233    );
 2234
 2235    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2236    cx.assert_editor_state(
 2237        &r#"
 2238        one
 2239        two
 2240        three
 2241        ˇfour
 2242        five
 2243        sixˇ
 2244        seven
 2245        eight
 2246        nine
 2247        ten
 2248        "#
 2249        .unindent(),
 2250    );
 2251
 2252    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2253    cx.assert_editor_state(
 2254        &r#"
 2255        ˇone
 2256        two
 2257        threeˇ
 2258        four
 2259        five
 2260        six
 2261        seven
 2262        eight
 2263        nine
 2264        ten
 2265        "#
 2266        .unindent(),
 2267    );
 2268
 2269    // Test select collapsing
 2270    cx.update_editor(|editor, window, cx| {
 2271        editor.move_page_down(&MovePageDown::default(), window, cx);
 2272        editor.move_page_down(&MovePageDown::default(), window, cx);
 2273        editor.move_page_down(&MovePageDown::default(), window, cx);
 2274    });
 2275    cx.assert_editor_state(
 2276        &r#"
 2277        one
 2278        two
 2279        three
 2280        four
 2281        five
 2282        six
 2283        seven
 2284        eight
 2285        nine
 2286        ˇten
 2287        ˇ"#
 2288        .unindent(),
 2289    );
 2290}
 2291
 2292#[gpui::test]
 2293async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2294    init_test(cx, |_| {});
 2295    let mut cx = EditorTestContext::new(cx).await;
 2296    cx.set_state("one «two threeˇ» four");
 2297    cx.update_editor(|editor, window, cx| {
 2298        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2299        assert_eq!(editor.text(cx), " four");
 2300    });
 2301}
 2302
 2303#[gpui::test]
 2304fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2305    init_test(cx, |_| {});
 2306
 2307    let editor = cx.add_window(|window, cx| {
 2308        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2309        build_editor(buffer.clone(), window, cx)
 2310    });
 2311
 2312    _ = editor.update(cx, |editor, window, cx| {
 2313        editor.change_selections(None, window, cx, |s| {
 2314            s.select_display_ranges([
 2315                // an empty selection - the preceding word fragment is deleted
 2316                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2317                // characters selected - they are deleted
 2318                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2319            ])
 2320        });
 2321        editor.delete_to_previous_word_start(
 2322            &DeleteToPreviousWordStart {
 2323                ignore_newlines: false,
 2324            },
 2325            window,
 2326            cx,
 2327        );
 2328        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2329    });
 2330
 2331    _ = editor.update(cx, |editor, window, cx| {
 2332        editor.change_selections(None, window, cx, |s| {
 2333            s.select_display_ranges([
 2334                // an empty selection - the following word fragment is deleted
 2335                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2336                // characters selected - they are deleted
 2337                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2338            ])
 2339        });
 2340        editor.delete_to_next_word_end(
 2341            &DeleteToNextWordEnd {
 2342                ignore_newlines: false,
 2343            },
 2344            window,
 2345            cx,
 2346        );
 2347        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2348    });
 2349}
 2350
 2351#[gpui::test]
 2352fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2353    init_test(cx, |_| {});
 2354
 2355    let editor = cx.add_window(|window, cx| {
 2356        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2357        build_editor(buffer.clone(), window, cx)
 2358    });
 2359    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2360        ignore_newlines: false,
 2361    };
 2362    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2363        ignore_newlines: true,
 2364    };
 2365
 2366    _ = editor.update(cx, |editor, window, cx| {
 2367        editor.change_selections(None, window, cx, |s| {
 2368            s.select_display_ranges([
 2369                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2370            ])
 2371        });
 2372        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2373        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2374        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2375        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2376        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2377        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2378        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2379        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2380        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2381        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2382        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2383        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2384    });
 2385}
 2386
 2387#[gpui::test]
 2388fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2389    init_test(cx, |_| {});
 2390
 2391    let editor = cx.add_window(|window, cx| {
 2392        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2393        build_editor(buffer.clone(), window, cx)
 2394    });
 2395    let del_to_next_word_end = DeleteToNextWordEnd {
 2396        ignore_newlines: false,
 2397    };
 2398    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2399        ignore_newlines: true,
 2400    };
 2401
 2402    _ = editor.update(cx, |editor, window, cx| {
 2403        editor.change_selections(None, window, cx, |s| {
 2404            s.select_display_ranges([
 2405                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2406            ])
 2407        });
 2408        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2409        assert_eq!(
 2410            editor.buffer.read(cx).read(cx).text(),
 2411            "one\n   two\nthree\n   four"
 2412        );
 2413        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2414        assert_eq!(
 2415            editor.buffer.read(cx).read(cx).text(),
 2416            "\n   two\nthree\n   four"
 2417        );
 2418        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2419        assert_eq!(
 2420            editor.buffer.read(cx).read(cx).text(),
 2421            "two\nthree\n   four"
 2422        );
 2423        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2424        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2425        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2426        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2427        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2428        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2429    });
 2430}
 2431
 2432#[gpui::test]
 2433fn test_newline(cx: &mut TestAppContext) {
 2434    init_test(cx, |_| {});
 2435
 2436    let editor = cx.add_window(|window, cx| {
 2437        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2438        build_editor(buffer.clone(), window, cx)
 2439    });
 2440
 2441    _ = editor.update(cx, |editor, window, cx| {
 2442        editor.change_selections(None, window, cx, |s| {
 2443            s.select_display_ranges([
 2444                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2445                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2446                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2447            ])
 2448        });
 2449
 2450        editor.newline(&Newline, window, cx);
 2451        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2452    });
 2453}
 2454
 2455#[gpui::test]
 2456fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2457    init_test(cx, |_| {});
 2458
 2459    let editor = cx.add_window(|window, cx| {
 2460        let buffer = MultiBuffer::build_simple(
 2461            "
 2462                a
 2463                b(
 2464                    X
 2465                )
 2466                c(
 2467                    X
 2468                )
 2469            "
 2470            .unindent()
 2471            .as_str(),
 2472            cx,
 2473        );
 2474        let mut editor = build_editor(buffer.clone(), window, cx);
 2475        editor.change_selections(None, window, cx, |s| {
 2476            s.select_ranges([
 2477                Point::new(2, 4)..Point::new(2, 5),
 2478                Point::new(5, 4)..Point::new(5, 5),
 2479            ])
 2480        });
 2481        editor
 2482    });
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2486        editor.buffer.update(cx, |buffer, cx| {
 2487            buffer.edit(
 2488                [
 2489                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2490                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2491                ],
 2492                None,
 2493                cx,
 2494            );
 2495            assert_eq!(
 2496                buffer.read(cx).text(),
 2497                "
 2498                    a
 2499                    b()
 2500                    c()
 2501                "
 2502                .unindent()
 2503            );
 2504        });
 2505        assert_eq!(
 2506            editor.selections.ranges(cx),
 2507            &[
 2508                Point::new(1, 2)..Point::new(1, 2),
 2509                Point::new(2, 2)..Point::new(2, 2),
 2510            ],
 2511        );
 2512
 2513        editor.newline(&Newline, window, cx);
 2514        assert_eq!(
 2515            editor.text(cx),
 2516            "
 2517                a
 2518                b(
 2519                )
 2520                c(
 2521                )
 2522            "
 2523            .unindent()
 2524        );
 2525
 2526        // The selections are moved after the inserted newlines
 2527        assert_eq!(
 2528            editor.selections.ranges(cx),
 2529            &[
 2530                Point::new(2, 0)..Point::new(2, 0),
 2531                Point::new(4, 0)..Point::new(4, 0),
 2532            ],
 2533        );
 2534    });
 2535}
 2536
 2537#[gpui::test]
 2538async fn test_newline_above(cx: &mut TestAppContext) {
 2539    init_test(cx, |settings| {
 2540        settings.defaults.tab_size = NonZeroU32::new(4)
 2541    });
 2542
 2543    let language = Arc::new(
 2544        Language::new(
 2545            LanguageConfig::default(),
 2546            Some(tree_sitter_rust::LANGUAGE.into()),
 2547        )
 2548        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2549        .unwrap(),
 2550    );
 2551
 2552    let mut cx = EditorTestContext::new(cx).await;
 2553    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2554    cx.set_state(indoc! {"
 2555        const a: ˇA = (
 2556 2557                «const_functionˇ»(ˇ),
 2558                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2559 2560        ˇ);ˇ
 2561    "});
 2562
 2563    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2564    cx.assert_editor_state(indoc! {"
 2565        ˇ
 2566        const a: A = (
 2567            ˇ
 2568            (
 2569                ˇ
 2570                ˇ
 2571                const_function(),
 2572                ˇ
 2573                ˇ
 2574                ˇ
 2575                ˇ
 2576                something_else,
 2577                ˇ
 2578            )
 2579            ˇ
 2580            ˇ
 2581        );
 2582    "});
 2583}
 2584
 2585#[gpui::test]
 2586async fn test_newline_below(cx: &mut TestAppContext) {
 2587    init_test(cx, |settings| {
 2588        settings.defaults.tab_size = NonZeroU32::new(4)
 2589    });
 2590
 2591    let language = Arc::new(
 2592        Language::new(
 2593            LanguageConfig::default(),
 2594            Some(tree_sitter_rust::LANGUAGE.into()),
 2595        )
 2596        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2597        .unwrap(),
 2598    );
 2599
 2600    let mut cx = EditorTestContext::new(cx).await;
 2601    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2602    cx.set_state(indoc! {"
 2603        const a: ˇA = (
 2604 2605                «const_functionˇ»(ˇ),
 2606                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2607 2608        ˇ);ˇ
 2609    "});
 2610
 2611    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2612    cx.assert_editor_state(indoc! {"
 2613        const a: A = (
 2614            ˇ
 2615            (
 2616                ˇ
 2617                const_function(),
 2618                ˇ
 2619                ˇ
 2620                something_else,
 2621                ˇ
 2622                ˇ
 2623                ˇ
 2624                ˇ
 2625            )
 2626            ˇ
 2627        );
 2628        ˇ
 2629        ˇ
 2630    "});
 2631}
 2632
 2633#[gpui::test]
 2634async fn test_newline_comments(cx: &mut TestAppContext) {
 2635    init_test(cx, |settings| {
 2636        settings.defaults.tab_size = NonZeroU32::new(4)
 2637    });
 2638
 2639    let language = Arc::new(Language::new(
 2640        LanguageConfig {
 2641            line_comments: vec!["//".into()],
 2642            ..LanguageConfig::default()
 2643        },
 2644        None,
 2645    ));
 2646    {
 2647        let mut cx = EditorTestContext::new(cx).await;
 2648        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2649        cx.set_state(indoc! {"
 2650        // Fooˇ
 2651    "});
 2652
 2653        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2654        cx.assert_editor_state(indoc! {"
 2655        // Foo
 2656        //ˇ
 2657    "});
 2658        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2659        cx.set_state(indoc! {"
 2660        ˇ// Foo
 2661    "});
 2662        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2663        cx.assert_editor_state(indoc! {"
 2664
 2665        ˇ// Foo
 2666    "});
 2667    }
 2668    // Ensure that comment continuations can be disabled.
 2669    update_test_language_settings(cx, |settings| {
 2670        settings.defaults.extend_comment_on_newline = Some(false);
 2671    });
 2672    let mut cx = EditorTestContext::new(cx).await;
 2673    cx.set_state(indoc! {"
 2674        // Fooˇ
 2675    "});
 2676    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2677    cx.assert_editor_state(indoc! {"
 2678        // Foo
 2679        ˇ
 2680    "});
 2681}
 2682
 2683#[gpui::test]
 2684fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2685    init_test(cx, |_| {});
 2686
 2687    let editor = cx.add_window(|window, cx| {
 2688        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2689        let mut editor = build_editor(buffer.clone(), window, cx);
 2690        editor.change_selections(None, window, cx, |s| {
 2691            s.select_ranges([3..4, 11..12, 19..20])
 2692        });
 2693        editor
 2694    });
 2695
 2696    _ = editor.update(cx, |editor, window, cx| {
 2697        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2698        editor.buffer.update(cx, |buffer, cx| {
 2699            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2700            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2701        });
 2702        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2703
 2704        editor.insert("Z", window, cx);
 2705        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2706
 2707        // The selections are moved after the inserted characters
 2708        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2709    });
 2710}
 2711
 2712#[gpui::test]
 2713async fn test_tab(cx: &mut TestAppContext) {
 2714    init_test(cx, |settings| {
 2715        settings.defaults.tab_size = NonZeroU32::new(3)
 2716    });
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.set_state(indoc! {"
 2720        ˇabˇc
 2721        ˇ🏀ˇ🏀ˇefg
 2722 2723    "});
 2724    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2725    cx.assert_editor_state(indoc! {"
 2726           ˇab ˇc
 2727           ˇ🏀  ˇ🏀  ˇefg
 2728        d  ˇ
 2729    "});
 2730
 2731    cx.set_state(indoc! {"
 2732        a
 2733        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2734    "});
 2735    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2736    cx.assert_editor_state(indoc! {"
 2737        a
 2738           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2739    "});
 2740}
 2741
 2742#[gpui::test]
 2743async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2744    init_test(cx, |_| {});
 2745
 2746    let mut cx = EditorTestContext::new(cx).await;
 2747    let language = Arc::new(
 2748        Language::new(
 2749            LanguageConfig::default(),
 2750            Some(tree_sitter_rust::LANGUAGE.into()),
 2751        )
 2752        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2753        .unwrap(),
 2754    );
 2755    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2756
 2757    // cursors that are already at the suggested indent level insert
 2758    // a soft tab. cursors that are to the left of the suggested indent
 2759    // auto-indent their line.
 2760    cx.set_state(indoc! {"
 2761        ˇ
 2762        const a: B = (
 2763            c(
 2764                d(
 2765        ˇ
 2766                )
 2767        ˇ
 2768        ˇ    )
 2769        );
 2770    "});
 2771    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2772    cx.assert_editor_state(indoc! {"
 2773            ˇ
 2774        const a: B = (
 2775            c(
 2776                d(
 2777                    ˇ
 2778                )
 2779                ˇ
 2780            ˇ)
 2781        );
 2782    "});
 2783
 2784    // handle auto-indent when there are multiple cursors on the same line
 2785    cx.set_state(indoc! {"
 2786        const a: B = (
 2787            c(
 2788        ˇ    ˇ
 2789        ˇ    )
 2790        );
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        const a: B = (
 2795            c(
 2796                ˇ
 2797            ˇ)
 2798        );
 2799    "});
 2800}
 2801
 2802#[gpui::test]
 2803async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2804    init_test(cx, |settings| {
 2805        settings.defaults.tab_size = NonZeroU32::new(4)
 2806    });
 2807
 2808    let language = Arc::new(
 2809        Language::new(
 2810            LanguageConfig::default(),
 2811            Some(tree_sitter_rust::LANGUAGE.into()),
 2812        )
 2813        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2814        .unwrap(),
 2815    );
 2816
 2817    let mut cx = EditorTestContext::new(cx).await;
 2818    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2819    cx.set_state(indoc! {"
 2820        fn a() {
 2821            if b {
 2822        \t ˇc
 2823            }
 2824        }
 2825    "});
 2826
 2827    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2828    cx.assert_editor_state(indoc! {"
 2829        fn a() {
 2830            if b {
 2831                ˇc
 2832            }
 2833        }
 2834    "});
 2835}
 2836
 2837#[gpui::test]
 2838async fn test_indent_outdent(cx: &mut TestAppContext) {
 2839    init_test(cx, |settings| {
 2840        settings.defaults.tab_size = NonZeroU32::new(4);
 2841    });
 2842
 2843    let mut cx = EditorTestContext::new(cx).await;
 2844
 2845    cx.set_state(indoc! {"
 2846          «oneˇ» «twoˇ»
 2847        three
 2848         four
 2849    "});
 2850    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2851    cx.assert_editor_state(indoc! {"
 2852            «oneˇ» «twoˇ»
 2853        three
 2854         four
 2855    "});
 2856
 2857    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2858    cx.assert_editor_state(indoc! {"
 2859        «oneˇ» «twoˇ»
 2860        three
 2861         four
 2862    "});
 2863
 2864    // select across line ending
 2865    cx.set_state(indoc! {"
 2866        one two
 2867        t«hree
 2868        ˇ» four
 2869    "});
 2870    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2871    cx.assert_editor_state(indoc! {"
 2872        one two
 2873            t«hree
 2874        ˇ» four
 2875    "});
 2876
 2877    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2878    cx.assert_editor_state(indoc! {"
 2879        one two
 2880        t«hree
 2881        ˇ» four
 2882    "});
 2883
 2884    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2885    cx.set_state(indoc! {"
 2886        one two
 2887        ˇthree
 2888            four
 2889    "});
 2890    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2891    cx.assert_editor_state(indoc! {"
 2892        one two
 2893            ˇthree
 2894            four
 2895    "});
 2896
 2897    cx.set_state(indoc! {"
 2898        one two
 2899        ˇ    three
 2900            four
 2901    "});
 2902    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2903    cx.assert_editor_state(indoc! {"
 2904        one two
 2905        ˇthree
 2906            four
 2907    "});
 2908}
 2909
 2910#[gpui::test]
 2911async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 2912    init_test(cx, |settings| {
 2913        settings.defaults.hard_tabs = Some(true);
 2914    });
 2915
 2916    let mut cx = EditorTestContext::new(cx).await;
 2917
 2918    // select two ranges on one line
 2919    cx.set_state(indoc! {"
 2920        «oneˇ» «twoˇ»
 2921        three
 2922        four
 2923    "});
 2924    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        \t«oneˇ» «twoˇ»
 2927        three
 2928        four
 2929    "});
 2930    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2931    cx.assert_editor_state(indoc! {"
 2932        \t\t«oneˇ» «twoˇ»
 2933        three
 2934        four
 2935    "});
 2936    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2937    cx.assert_editor_state(indoc! {"
 2938        \t«oneˇ» «twoˇ»
 2939        three
 2940        four
 2941    "});
 2942    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2943    cx.assert_editor_state(indoc! {"
 2944        «oneˇ» «twoˇ»
 2945        three
 2946        four
 2947    "});
 2948
 2949    // select across a line ending
 2950    cx.set_state(indoc! {"
 2951        one two
 2952        t«hree
 2953        ˇ»four
 2954    "});
 2955    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        one two
 2958        \tt«hree
 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        \t\tt«hree
 2965        ˇ»four
 2966    "});
 2967    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2968    cx.assert_editor_state(indoc! {"
 2969        one two
 2970        \tt«hree
 2971        ˇ»four
 2972    "});
 2973    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2974    cx.assert_editor_state(indoc! {"
 2975        one two
 2976        t«hree
 2977        ˇ»four
 2978    "});
 2979
 2980    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2981    cx.set_state(indoc! {"
 2982        one two
 2983        ˇthree
 2984        four
 2985    "});
 2986    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2987    cx.assert_editor_state(indoc! {"
 2988        one two
 2989        ˇthree
 2990        four
 2991    "});
 2992    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        one two
 2995        \tˇthree
 2996        four
 2997    "});
 2998    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        one two
 3001        ˇthree
 3002        four
 3003    "});
 3004}
 3005
 3006#[gpui::test]
 3007fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3008    init_test(cx, |settings| {
 3009        settings.languages.extend([
 3010            (
 3011                "TOML".into(),
 3012                LanguageSettingsContent {
 3013                    tab_size: NonZeroU32::new(2),
 3014                    ..Default::default()
 3015                },
 3016            ),
 3017            (
 3018                "Rust".into(),
 3019                LanguageSettingsContent {
 3020                    tab_size: NonZeroU32::new(4),
 3021                    ..Default::default()
 3022                },
 3023            ),
 3024        ]);
 3025    });
 3026
 3027    let toml_language = Arc::new(Language::new(
 3028        LanguageConfig {
 3029            name: "TOML".into(),
 3030            ..Default::default()
 3031        },
 3032        None,
 3033    ));
 3034    let rust_language = Arc::new(Language::new(
 3035        LanguageConfig {
 3036            name: "Rust".into(),
 3037            ..Default::default()
 3038        },
 3039        None,
 3040    ));
 3041
 3042    let toml_buffer =
 3043        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3044    let rust_buffer =
 3045        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3046    let multibuffer = cx.new(|cx| {
 3047        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3048        multibuffer.push_excerpts(
 3049            toml_buffer.clone(),
 3050            [ExcerptRange {
 3051                context: Point::new(0, 0)..Point::new(2, 0),
 3052                primary: None,
 3053            }],
 3054            cx,
 3055        );
 3056        multibuffer.push_excerpts(
 3057            rust_buffer.clone(),
 3058            [ExcerptRange {
 3059                context: Point::new(0, 0)..Point::new(1, 0),
 3060                primary: None,
 3061            }],
 3062            cx,
 3063        );
 3064        multibuffer
 3065    });
 3066
 3067    cx.add_window(|window, cx| {
 3068        let mut editor = build_editor(multibuffer, window, cx);
 3069
 3070        assert_eq!(
 3071            editor.text(cx),
 3072            indoc! {"
 3073                a = 1
 3074                b = 2
 3075
 3076                const c: usize = 3;
 3077            "}
 3078        );
 3079
 3080        select_ranges(
 3081            &mut editor,
 3082            indoc! {"
 3083                «aˇ» = 1
 3084                b = 2
 3085
 3086                «const c:ˇ» usize = 3;
 3087            "},
 3088            window,
 3089            cx,
 3090        );
 3091
 3092        editor.tab(&Tab, window, cx);
 3093        assert_text_with_selections(
 3094            &mut editor,
 3095            indoc! {"
 3096                  «aˇ» = 1
 3097                b = 2
 3098
 3099                    «const c:ˇ» usize = 3;
 3100            "},
 3101            cx,
 3102        );
 3103        editor.tab_prev(&TabPrev, window, cx);
 3104        assert_text_with_selections(
 3105            &mut editor,
 3106            indoc! {"
 3107                «aˇ» = 1
 3108                b = 2
 3109
 3110                «const c:ˇ» usize = 3;
 3111            "},
 3112            cx,
 3113        );
 3114
 3115        editor
 3116    });
 3117}
 3118
 3119#[gpui::test]
 3120async fn test_backspace(cx: &mut TestAppContext) {
 3121    init_test(cx, |_| {});
 3122
 3123    let mut cx = EditorTestContext::new(cx).await;
 3124
 3125    // Basic backspace
 3126    cx.set_state(indoc! {"
 3127        onˇe two three
 3128        fou«rˇ» five six
 3129        seven «ˇeight nine
 3130        »ten
 3131    "});
 3132    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3133    cx.assert_editor_state(indoc! {"
 3134        oˇe two three
 3135        fouˇ five six
 3136        seven ˇten
 3137    "});
 3138
 3139    // Test backspace inside and around indents
 3140    cx.set_state(indoc! {"
 3141        zero
 3142            ˇone
 3143                ˇtwo
 3144            ˇ ˇ ˇ  three
 3145        ˇ  ˇ  four
 3146    "});
 3147    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3148    cx.assert_editor_state(indoc! {"
 3149        zero
 3150        ˇone
 3151            ˇtwo
 3152        ˇ  threeˇ  four
 3153    "});
 3154
 3155    // Test backspace with line_mode set to true
 3156    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3157    cx.set_state(indoc! {"
 3158        The ˇquick ˇbrown
 3159        fox jumps over
 3160        the lazy dog
 3161        ˇThe qu«ick bˇ»rown"});
 3162    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        ˇfox jumps over
 3165        the lazy dogˇ"});
 3166}
 3167
 3168#[gpui::test]
 3169async fn test_delete(cx: &mut TestAppContext) {
 3170    init_test(cx, |_| {});
 3171
 3172    let mut cx = EditorTestContext::new(cx).await;
 3173    cx.set_state(indoc! {"
 3174        onˇe two three
 3175        fou«rˇ» five six
 3176        seven «ˇeight nine
 3177        »ten
 3178    "});
 3179    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3180    cx.assert_editor_state(indoc! {"
 3181        onˇ two three
 3182        fouˇ five six
 3183        seven ˇten
 3184    "});
 3185
 3186    // Test backspace with line_mode set to true
 3187    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3188    cx.set_state(indoc! {"
 3189        The ˇquick ˇbrown
 3190        fox «ˇjum»ps over
 3191        the lazy dog
 3192        ˇThe qu«ick bˇ»rown"});
 3193    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3194    cx.assert_editor_state("ˇthe lazy dogˇ");
 3195}
 3196
 3197#[gpui::test]
 3198fn test_delete_line(cx: &mut TestAppContext) {
 3199    init_test(cx, |_| {});
 3200
 3201    let editor = cx.add_window(|window, cx| {
 3202        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3203        build_editor(buffer, window, cx)
 3204    });
 3205    _ = editor.update(cx, |editor, window, cx| {
 3206        editor.change_selections(None, window, cx, |s| {
 3207            s.select_display_ranges([
 3208                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3209                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3210                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3211            ])
 3212        });
 3213        editor.delete_line(&DeleteLine, window, cx);
 3214        assert_eq!(editor.display_text(cx), "ghi");
 3215        assert_eq!(
 3216            editor.selections.display_ranges(cx),
 3217            vec![
 3218                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3219                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3220            ]
 3221        );
 3222    });
 3223
 3224    let editor = cx.add_window(|window, cx| {
 3225        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3226        build_editor(buffer, window, cx)
 3227    });
 3228    _ = editor.update(cx, |editor, window, cx| {
 3229        editor.change_selections(None, window, cx, |s| {
 3230            s.select_display_ranges([
 3231                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3232            ])
 3233        });
 3234        editor.delete_line(&DeleteLine, window, cx);
 3235        assert_eq!(editor.display_text(cx), "ghi\n");
 3236        assert_eq!(
 3237            editor.selections.display_ranges(cx),
 3238            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3239        );
 3240    });
 3241}
 3242
 3243#[gpui::test]
 3244fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3245    init_test(cx, |_| {});
 3246
 3247    cx.add_window(|window, cx| {
 3248        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3249        let mut editor = build_editor(buffer.clone(), window, cx);
 3250        let buffer = buffer.read(cx).as_singleton().unwrap();
 3251
 3252        assert_eq!(
 3253            editor.selections.ranges::<Point>(cx),
 3254            &[Point::new(0, 0)..Point::new(0, 0)]
 3255        );
 3256
 3257        // When on single line, replace newline at end by space
 3258        editor.join_lines(&JoinLines, window, cx);
 3259        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3260        assert_eq!(
 3261            editor.selections.ranges::<Point>(cx),
 3262            &[Point::new(0, 3)..Point::new(0, 3)]
 3263        );
 3264
 3265        // When multiple lines are selected, remove newlines that are spanned by the selection
 3266        editor.change_selections(None, window, cx, |s| {
 3267            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3268        });
 3269        editor.join_lines(&JoinLines, window, cx);
 3270        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3271        assert_eq!(
 3272            editor.selections.ranges::<Point>(cx),
 3273            &[Point::new(0, 11)..Point::new(0, 11)]
 3274        );
 3275
 3276        // Undo should be transactional
 3277        editor.undo(&Undo, window, cx);
 3278        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3279        assert_eq!(
 3280            editor.selections.ranges::<Point>(cx),
 3281            &[Point::new(0, 5)..Point::new(2, 2)]
 3282        );
 3283
 3284        // When joining an empty line don't insert a space
 3285        editor.change_selections(None, window, cx, |s| {
 3286            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3287        });
 3288        editor.join_lines(&JoinLines, window, cx);
 3289        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3290        assert_eq!(
 3291            editor.selections.ranges::<Point>(cx),
 3292            [Point::new(2, 3)..Point::new(2, 3)]
 3293        );
 3294
 3295        // We can remove trailing newlines
 3296        editor.join_lines(&JoinLines, window, cx);
 3297        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3298        assert_eq!(
 3299            editor.selections.ranges::<Point>(cx),
 3300            [Point::new(2, 3)..Point::new(2, 3)]
 3301        );
 3302
 3303        // We don't blow up on the last line
 3304        editor.join_lines(&JoinLines, window, cx);
 3305        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3306        assert_eq!(
 3307            editor.selections.ranges::<Point>(cx),
 3308            [Point::new(2, 3)..Point::new(2, 3)]
 3309        );
 3310
 3311        // reset to test indentation
 3312        editor.buffer.update(cx, |buffer, cx| {
 3313            buffer.edit(
 3314                [
 3315                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3316                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3317                ],
 3318                None,
 3319                cx,
 3320            )
 3321        });
 3322
 3323        // We remove any leading spaces
 3324        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3325        editor.change_selections(None, window, cx, |s| {
 3326            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3327        });
 3328        editor.join_lines(&JoinLines, window, cx);
 3329        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3330
 3331        // We don't insert a space for a line containing only spaces
 3332        editor.join_lines(&JoinLines, window, cx);
 3333        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3334
 3335        // We ignore any leading tabs
 3336        editor.join_lines(&JoinLines, window, cx);
 3337        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3338
 3339        editor
 3340    });
 3341}
 3342
 3343#[gpui::test]
 3344fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3345    init_test(cx, |_| {});
 3346
 3347    cx.add_window(|window, cx| {
 3348        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3349        let mut editor = build_editor(buffer.clone(), window, cx);
 3350        let buffer = buffer.read(cx).as_singleton().unwrap();
 3351
 3352        editor.change_selections(None, window, cx, |s| {
 3353            s.select_ranges([
 3354                Point::new(0, 2)..Point::new(1, 1),
 3355                Point::new(1, 2)..Point::new(1, 2),
 3356                Point::new(3, 1)..Point::new(3, 2),
 3357            ])
 3358        });
 3359
 3360        editor.join_lines(&JoinLines, window, cx);
 3361        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3362
 3363        assert_eq!(
 3364            editor.selections.ranges::<Point>(cx),
 3365            [
 3366                Point::new(0, 7)..Point::new(0, 7),
 3367                Point::new(1, 3)..Point::new(1, 3)
 3368            ]
 3369        );
 3370        editor
 3371    });
 3372}
 3373
 3374#[gpui::test]
 3375async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3376    init_test(cx, |_| {});
 3377
 3378    let mut cx = EditorTestContext::new(cx).await;
 3379
 3380    let diff_base = r#"
 3381        Line 0
 3382        Line 1
 3383        Line 2
 3384        Line 3
 3385        "#
 3386    .unindent();
 3387
 3388    cx.set_state(
 3389        &r#"
 3390        ˇLine 0
 3391        Line 1
 3392        Line 2
 3393        Line 3
 3394        "#
 3395        .unindent(),
 3396    );
 3397
 3398    cx.set_head_text(&diff_base);
 3399    executor.run_until_parked();
 3400
 3401    // Join lines
 3402    cx.update_editor(|editor, window, cx| {
 3403        editor.join_lines(&JoinLines, window, cx);
 3404    });
 3405    executor.run_until_parked();
 3406
 3407    cx.assert_editor_state(
 3408        &r#"
 3409        Line 0ˇ Line 1
 3410        Line 2
 3411        Line 3
 3412        "#
 3413        .unindent(),
 3414    );
 3415    // Join again
 3416    cx.update_editor(|editor, window, cx| {
 3417        editor.join_lines(&JoinLines, window, cx);
 3418    });
 3419    executor.run_until_parked();
 3420
 3421    cx.assert_editor_state(
 3422        &r#"
 3423        Line 0 Line 1ˇ Line 2
 3424        Line 3
 3425        "#
 3426        .unindent(),
 3427    );
 3428}
 3429
 3430#[gpui::test]
 3431async fn test_custom_newlines_cause_no_false_positive_diffs(
 3432    executor: BackgroundExecutor,
 3433    cx: &mut TestAppContext,
 3434) {
 3435    init_test(cx, |_| {});
 3436    let mut cx = EditorTestContext::new(cx).await;
 3437    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3438    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3439    executor.run_until_parked();
 3440
 3441    cx.update_editor(|editor, window, cx| {
 3442        let snapshot = editor.snapshot(window, cx);
 3443        assert_eq!(
 3444            snapshot
 3445                .buffer_snapshot
 3446                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3447                .collect::<Vec<_>>(),
 3448            Vec::new(),
 3449            "Should not have any diffs for files with custom newlines"
 3450        );
 3451    });
 3452}
 3453
 3454#[gpui::test]
 3455async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3456    init_test(cx, |_| {});
 3457
 3458    let mut cx = EditorTestContext::new(cx).await;
 3459
 3460    // Test sort_lines_case_insensitive()
 3461    cx.set_state(indoc! {"
 3462        «z
 3463        y
 3464        x
 3465        Z
 3466        Y
 3467        Xˇ»
 3468    "});
 3469    cx.update_editor(|e, window, cx| {
 3470        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3471    });
 3472    cx.assert_editor_state(indoc! {"
 3473        «x
 3474        X
 3475        y
 3476        Y
 3477        z
 3478        Zˇ»
 3479    "});
 3480
 3481    // Test reverse_lines()
 3482    cx.set_state(indoc! {"
 3483        «5
 3484        4
 3485        3
 3486        2
 3487        1ˇ»
 3488    "});
 3489    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        «1
 3492        2
 3493        3
 3494        4
 3495        5ˇ»
 3496    "});
 3497
 3498    // Skip testing shuffle_line()
 3499
 3500    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3501    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3502
 3503    // Don't manipulate when cursor is on single line, but expand the selection
 3504    cx.set_state(indoc! {"
 3505        ddˇdd
 3506        ccc
 3507        bb
 3508        a
 3509    "});
 3510    cx.update_editor(|e, window, cx| {
 3511        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3512    });
 3513    cx.assert_editor_state(indoc! {"
 3514        «ddddˇ»
 3515        ccc
 3516        bb
 3517        a
 3518    "});
 3519
 3520    // Basic manipulate case
 3521    // Start selection moves to column 0
 3522    // End of selection shrinks to fit shorter line
 3523    cx.set_state(indoc! {"
 3524        dd«d
 3525        ccc
 3526        bb
 3527        aaaaaˇ»
 3528    "});
 3529    cx.update_editor(|e, window, cx| {
 3530        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3531    });
 3532    cx.assert_editor_state(indoc! {"
 3533        «aaaaa
 3534        bb
 3535        ccc
 3536        dddˇ»
 3537    "});
 3538
 3539    // Manipulate case with newlines
 3540    cx.set_state(indoc! {"
 3541        dd«d
 3542        ccc
 3543
 3544        bb
 3545        aaaaa
 3546
 3547        ˇ»
 3548    "});
 3549    cx.update_editor(|e, window, cx| {
 3550        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3551    });
 3552    cx.assert_editor_state(indoc! {"
 3553        «
 3554
 3555        aaaaa
 3556        bb
 3557        ccc
 3558        dddˇ»
 3559
 3560    "});
 3561
 3562    // Adding new line
 3563    cx.set_state(indoc! {"
 3564        aa«a
 3565        bbˇ»b
 3566    "});
 3567    cx.update_editor(|e, window, cx| {
 3568        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3569    });
 3570    cx.assert_editor_state(indoc! {"
 3571        «aaa
 3572        bbb
 3573        added_lineˇ»
 3574    "});
 3575
 3576    // Removing line
 3577    cx.set_state(indoc! {"
 3578        aa«a
 3579        bbbˇ»
 3580    "});
 3581    cx.update_editor(|e, window, cx| {
 3582        e.manipulate_lines(window, cx, |lines| {
 3583            lines.pop();
 3584        })
 3585    });
 3586    cx.assert_editor_state(indoc! {"
 3587        «aaaˇ»
 3588    "});
 3589
 3590    // Removing all lines
 3591    cx.set_state(indoc! {"
 3592        aa«a
 3593        bbbˇ»
 3594    "});
 3595    cx.update_editor(|e, window, cx| {
 3596        e.manipulate_lines(window, cx, |lines| {
 3597            lines.drain(..);
 3598        })
 3599    });
 3600    cx.assert_editor_state(indoc! {"
 3601        ˇ
 3602    "});
 3603}
 3604
 3605#[gpui::test]
 3606async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3607    init_test(cx, |_| {});
 3608
 3609    let mut cx = EditorTestContext::new(cx).await;
 3610
 3611    // Consider continuous selection as single selection
 3612    cx.set_state(indoc! {"
 3613        Aaa«aa
 3614        cˇ»c«c
 3615        bb
 3616        aaaˇ»aa
 3617    "});
 3618    cx.update_editor(|e, window, cx| {
 3619        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3620    });
 3621    cx.assert_editor_state(indoc! {"
 3622        «Aaaaa
 3623        ccc
 3624        bb
 3625        aaaaaˇ»
 3626    "});
 3627
 3628    cx.set_state(indoc! {"
 3629        Aaa«aa
 3630        cˇ»c«c
 3631        bb
 3632        aaaˇ»aa
 3633    "});
 3634    cx.update_editor(|e, window, cx| {
 3635        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3636    });
 3637    cx.assert_editor_state(indoc! {"
 3638        «Aaaaa
 3639        ccc
 3640        bbˇ»
 3641    "});
 3642
 3643    // Consider non continuous selection as distinct dedup operations
 3644    cx.set_state(indoc! {"
 3645        «aaaaa
 3646        bb
 3647        aaaaa
 3648        aaaaaˇ»
 3649
 3650        aaa«aaˇ»
 3651    "});
 3652    cx.update_editor(|e, window, cx| {
 3653        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3654    });
 3655    cx.assert_editor_state(indoc! {"
 3656        «aaaaa
 3657        bbˇ»
 3658
 3659        «aaaaaˇ»
 3660    "});
 3661}
 3662
 3663#[gpui::test]
 3664async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3665    init_test(cx, |_| {});
 3666
 3667    let mut cx = EditorTestContext::new(cx).await;
 3668
 3669    cx.set_state(indoc! {"
 3670        «Aaa
 3671        aAa
 3672        Aaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «Aaa
 3679        aAaˇ»
 3680    "});
 3681
 3682    cx.set_state(indoc! {"
 3683        «Aaa
 3684        aAa
 3685        aaAˇ»
 3686    "});
 3687    cx.update_editor(|e, window, cx| {
 3688        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        «Aaaˇ»
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Manipulate with multiple selections on a single line
 3702    cx.set_state(indoc! {"
 3703        dd«dd
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «aaaaa
 3713        bb
 3714        ccc
 3715        ddddˇ»
 3716    "});
 3717
 3718    // Manipulate with multiple disjoin selections
 3719    cx.set_state(indoc! {"
 3720 3721        4
 3722        3
 3723        2
 3724        1ˇ»
 3725
 3726        dd«dd
 3727        ccc
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «1
 3736        2
 3737        3
 3738        4
 3739        5ˇ»
 3740
 3741        «aaaaa
 3742        bb
 3743        ccc
 3744        ddddˇ»
 3745    "});
 3746
 3747    // Adding lines on each selection
 3748    cx.set_state(indoc! {"
 3749 3750        1ˇ»
 3751
 3752        bb«bb
 3753        aaaˇ»aa
 3754    "});
 3755    cx.update_editor(|e, window, cx| {
 3756        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3757    });
 3758    cx.assert_editor_state(indoc! {"
 3759        «2
 3760        1
 3761        added lineˇ»
 3762
 3763        «bbbb
 3764        aaaaa
 3765        added lineˇ»
 3766    "});
 3767
 3768    // Removing lines on each selection
 3769    cx.set_state(indoc! {"
 3770 3771        1ˇ»
 3772
 3773        bb«bb
 3774        aaaˇ»aa
 3775    "});
 3776    cx.update_editor(|e, window, cx| {
 3777        e.manipulate_lines(window, cx, |lines| {
 3778            lines.pop();
 3779        })
 3780    });
 3781    cx.assert_editor_state(indoc! {"
 3782        «2ˇ»
 3783
 3784        «bbbbˇ»
 3785    "});
 3786}
 3787
 3788#[gpui::test]
 3789async fn test_manipulate_text(cx: &mut TestAppContext) {
 3790    init_test(cx, |_| {});
 3791
 3792    let mut cx = EditorTestContext::new(cx).await;
 3793
 3794    // Test convert_to_upper_case()
 3795    cx.set_state(indoc! {"
 3796        «hello worldˇ»
 3797    "});
 3798    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3799    cx.assert_editor_state(indoc! {"
 3800        «HELLO WORLDˇ»
 3801    "});
 3802
 3803    // Test convert_to_lower_case()
 3804    cx.set_state(indoc! {"
 3805        «HELLO WORLDˇ»
 3806    "});
 3807    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3808    cx.assert_editor_state(indoc! {"
 3809        «hello worldˇ»
 3810    "});
 3811
 3812    // Test multiple line, single selection case
 3813    cx.set_state(indoc! {"
 3814        «The quick brown
 3815        fox jumps over
 3816        the lazy dogˇ»
 3817    "});
 3818    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3819    cx.assert_editor_state(indoc! {"
 3820        «The Quick Brown
 3821        Fox Jumps Over
 3822        The Lazy Dogˇ»
 3823    "});
 3824
 3825    // Test multiple line, single selection case
 3826    cx.set_state(indoc! {"
 3827        «The quick brown
 3828        fox jumps over
 3829        the lazy dogˇ»
 3830    "});
 3831    cx.update_editor(|e, window, cx| {
 3832        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3833    });
 3834    cx.assert_editor_state(indoc! {"
 3835        «TheQuickBrown
 3836        FoxJumpsOver
 3837        TheLazyDogˇ»
 3838    "});
 3839
 3840    // From here on out, test more complex cases of manipulate_text()
 3841
 3842    // Test no selection case - should affect words cursors are in
 3843    // Cursor at beginning, middle, and end of word
 3844    cx.set_state(indoc! {"
 3845        ˇhello big beauˇtiful worldˇ
 3846    "});
 3847    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3848    cx.assert_editor_state(indoc! {"
 3849        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3850    "});
 3851
 3852    // Test multiple selections on a single line and across multiple lines
 3853    cx.set_state(indoc! {"
 3854        «Theˇ» quick «brown
 3855        foxˇ» jumps «overˇ»
 3856        the «lazyˇ» dog
 3857    "});
 3858    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3859    cx.assert_editor_state(indoc! {"
 3860        «THEˇ» quick «BROWN
 3861        FOXˇ» jumps «OVERˇ»
 3862        the «LAZYˇ» dog
 3863    "});
 3864
 3865    // Test case where text length grows
 3866    cx.set_state(indoc! {"
 3867        «tschüߡ»
 3868    "});
 3869    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3870    cx.assert_editor_state(indoc! {"
 3871        «TSCHÜSSˇ»
 3872    "});
 3873
 3874    // Test to make sure we don't crash when text shrinks
 3875    cx.set_state(indoc! {"
 3876        aaa_bbbˇ
 3877    "});
 3878    cx.update_editor(|e, window, cx| {
 3879        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3880    });
 3881    cx.assert_editor_state(indoc! {"
 3882        «aaaBbbˇ»
 3883    "});
 3884
 3885    // Test to make sure we all aware of the fact that each word can grow and shrink
 3886    // Final selections should be aware of this fact
 3887    cx.set_state(indoc! {"
 3888        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3889    "});
 3890    cx.update_editor(|e, window, cx| {
 3891        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3892    });
 3893    cx.assert_editor_state(indoc! {"
 3894        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3895    "});
 3896
 3897    cx.set_state(indoc! {"
 3898        «hElLo, WoRld!ˇ»
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «HeLlO, wOrLD!ˇ»
 3905    "});
 3906}
 3907
 3908#[gpui::test]
 3909fn test_duplicate_line(cx: &mut TestAppContext) {
 3910    init_test(cx, |_| {});
 3911
 3912    let editor = cx.add_window(|window, cx| {
 3913        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3914        build_editor(buffer, window, cx)
 3915    });
 3916    _ = editor.update(cx, |editor, window, cx| {
 3917        editor.change_selections(None, window, cx, |s| {
 3918            s.select_display_ranges([
 3919                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3920                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3921                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3922                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3923            ])
 3924        });
 3925        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3926        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3927        assert_eq!(
 3928            editor.selections.display_ranges(cx),
 3929            vec![
 3930                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3931                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3932                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3933                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3934            ]
 3935        );
 3936    });
 3937
 3938    let editor = cx.add_window(|window, cx| {
 3939        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3940        build_editor(buffer, window, cx)
 3941    });
 3942    _ = editor.update(cx, |editor, window, cx| {
 3943        editor.change_selections(None, window, cx, |s| {
 3944            s.select_display_ranges([
 3945                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3946                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3947            ])
 3948        });
 3949        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3950        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3951        assert_eq!(
 3952            editor.selections.display_ranges(cx),
 3953            vec![
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3955                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3956            ]
 3957        );
 3958    });
 3959
 3960    // With `move_upwards` the selections stay in place, except for
 3961    // the lines inserted above them
 3962    let editor = cx.add_window(|window, cx| {
 3963        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3964        build_editor(buffer, window, cx)
 3965    });
 3966    _ = editor.update(cx, |editor, window, cx| {
 3967        editor.change_selections(None, window, cx, |s| {
 3968            s.select_display_ranges([
 3969                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3970                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3971                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3972                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3973            ])
 3974        });
 3975        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3976        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3977        assert_eq!(
 3978            editor.selections.display_ranges(cx),
 3979            vec![
 3980                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3981                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3982                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3983                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3984            ]
 3985        );
 3986    });
 3987
 3988    let editor = cx.add_window(|window, cx| {
 3989        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3990        build_editor(buffer, window, cx)
 3991    });
 3992    _ = editor.update(cx, |editor, window, cx| {
 3993        editor.change_selections(None, window, cx, |s| {
 3994            s.select_display_ranges([
 3995                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3996                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3997            ])
 3998        });
 3999        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4000        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4001        assert_eq!(
 4002            editor.selections.display_ranges(cx),
 4003            vec![
 4004                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4005                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4006            ]
 4007        );
 4008    });
 4009
 4010    let editor = cx.add_window(|window, cx| {
 4011        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4012        build_editor(buffer, window, cx)
 4013    });
 4014    _ = editor.update(cx, |editor, window, cx| {
 4015        editor.change_selections(None, window, cx, |s| {
 4016            s.select_display_ranges([
 4017                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4018                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4019            ])
 4020        });
 4021        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4022        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4023        assert_eq!(
 4024            editor.selections.display_ranges(cx),
 4025            vec![
 4026                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4027                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4028            ]
 4029        );
 4030    });
 4031}
 4032
 4033#[gpui::test]
 4034fn test_move_line_up_down(cx: &mut TestAppContext) {
 4035    init_test(cx, |_| {});
 4036
 4037    let editor = cx.add_window(|window, cx| {
 4038        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4039        build_editor(buffer, window, cx)
 4040    });
 4041    _ = editor.update(cx, |editor, window, cx| {
 4042        editor.fold_creases(
 4043            vec![
 4044                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4045                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4046                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4047            ],
 4048            true,
 4049            window,
 4050            cx,
 4051        );
 4052        editor.change_selections(None, window, cx, |s| {
 4053            s.select_display_ranges([
 4054                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4055                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4056                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4057                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4058            ])
 4059        });
 4060        assert_eq!(
 4061            editor.display_text(cx),
 4062            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4063        );
 4064
 4065        editor.move_line_up(&MoveLineUp, window, cx);
 4066        assert_eq!(
 4067            editor.display_text(cx),
 4068            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4069        );
 4070        assert_eq!(
 4071            editor.selections.display_ranges(cx),
 4072            vec![
 4073                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4074                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4075                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4076                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4077            ]
 4078        );
 4079    });
 4080
 4081    _ = editor.update(cx, |editor, window, cx| {
 4082        editor.move_line_down(&MoveLineDown, window, cx);
 4083        assert_eq!(
 4084            editor.display_text(cx),
 4085            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4086        );
 4087        assert_eq!(
 4088            editor.selections.display_ranges(cx),
 4089            vec![
 4090                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4091                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4092                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4093                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4094            ]
 4095        );
 4096    });
 4097
 4098    _ = editor.update(cx, |editor, window, cx| {
 4099        editor.move_line_down(&MoveLineDown, window, cx);
 4100        assert_eq!(
 4101            editor.display_text(cx),
 4102            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4103        );
 4104        assert_eq!(
 4105            editor.selections.display_ranges(cx),
 4106            vec![
 4107                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4108                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4109                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4110                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4111            ]
 4112        );
 4113    });
 4114
 4115    _ = editor.update(cx, |editor, window, cx| {
 4116        editor.move_line_up(&MoveLineUp, window, cx);
 4117        assert_eq!(
 4118            editor.display_text(cx),
 4119            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4120        );
 4121        assert_eq!(
 4122            editor.selections.display_ranges(cx),
 4123            vec![
 4124                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4125                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4126                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4127                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4128            ]
 4129        );
 4130    });
 4131}
 4132
 4133#[gpui::test]
 4134fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4135    init_test(cx, |_| {});
 4136
 4137    let editor = cx.add_window(|window, cx| {
 4138        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4139        build_editor(buffer, window, cx)
 4140    });
 4141    _ = editor.update(cx, |editor, window, cx| {
 4142        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4143        editor.insert_blocks(
 4144            [BlockProperties {
 4145                style: BlockStyle::Fixed,
 4146                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4147                height: 1,
 4148                render: Arc::new(|_| div().into_any()),
 4149                priority: 0,
 4150            }],
 4151            Some(Autoscroll::fit()),
 4152            cx,
 4153        );
 4154        editor.change_selections(None, window, cx, |s| {
 4155            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4156        });
 4157        editor.move_line_down(&MoveLineDown, window, cx);
 4158    });
 4159}
 4160
 4161#[gpui::test]
 4162async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4163    init_test(cx, |_| {});
 4164
 4165    let mut cx = EditorTestContext::new(cx).await;
 4166    cx.set_state(
 4167        &"
 4168            ˇzero
 4169            one
 4170            two
 4171            three
 4172            four
 4173            five
 4174        "
 4175        .unindent(),
 4176    );
 4177
 4178    // Create a four-line block that replaces three lines of text.
 4179    cx.update_editor(|editor, window, cx| {
 4180        let snapshot = editor.snapshot(window, cx);
 4181        let snapshot = &snapshot.buffer_snapshot;
 4182        let placement = BlockPlacement::Replace(
 4183            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4184        );
 4185        editor.insert_blocks(
 4186            [BlockProperties {
 4187                placement,
 4188                height: 4,
 4189                style: BlockStyle::Sticky,
 4190                render: Arc::new(|_| gpui::div().into_any_element()),
 4191                priority: 0,
 4192            }],
 4193            None,
 4194            cx,
 4195        );
 4196    });
 4197
 4198    // Move down so that the cursor touches the block.
 4199    cx.update_editor(|editor, window, cx| {
 4200        editor.move_down(&Default::default(), window, cx);
 4201    });
 4202    cx.assert_editor_state(
 4203        &"
 4204            zero
 4205            «one
 4206            two
 4207            threeˇ»
 4208            four
 4209            five
 4210        "
 4211        .unindent(),
 4212    );
 4213
 4214    // Move down past the block.
 4215    cx.update_editor(|editor, window, cx| {
 4216        editor.move_down(&Default::default(), window, cx);
 4217    });
 4218    cx.assert_editor_state(
 4219        &"
 4220            zero
 4221            one
 4222            two
 4223            three
 4224            ˇfour
 4225            five
 4226        "
 4227        .unindent(),
 4228    );
 4229}
 4230
 4231#[gpui::test]
 4232fn test_transpose(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    _ = cx.add_window(|window, cx| {
 4236        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4237        editor.set_style(EditorStyle::default(), window, cx);
 4238        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4239        editor.transpose(&Default::default(), window, cx);
 4240        assert_eq!(editor.text(cx), "bac");
 4241        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4242
 4243        editor.transpose(&Default::default(), window, cx);
 4244        assert_eq!(editor.text(cx), "bca");
 4245        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4246
 4247        editor.transpose(&Default::default(), window, cx);
 4248        assert_eq!(editor.text(cx), "bac");
 4249        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4250
 4251        editor
 4252    });
 4253
 4254    _ = cx.add_window(|window, cx| {
 4255        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4256        editor.set_style(EditorStyle::default(), window, cx);
 4257        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4258        editor.transpose(&Default::default(), window, cx);
 4259        assert_eq!(editor.text(cx), "acb\nde");
 4260        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4261
 4262        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4263        editor.transpose(&Default::default(), window, cx);
 4264        assert_eq!(editor.text(cx), "acbd\ne");
 4265        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4266
 4267        editor.transpose(&Default::default(), window, cx);
 4268        assert_eq!(editor.text(cx), "acbde\n");
 4269        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4270
 4271        editor.transpose(&Default::default(), window, cx);
 4272        assert_eq!(editor.text(cx), "acbd\ne");
 4273        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4274
 4275        editor
 4276    });
 4277
 4278    _ = cx.add_window(|window, cx| {
 4279        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4280        editor.set_style(EditorStyle::default(), window, cx);
 4281        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4282        editor.transpose(&Default::default(), window, cx);
 4283        assert_eq!(editor.text(cx), "bacd\ne");
 4284        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4285
 4286        editor.transpose(&Default::default(), window, cx);
 4287        assert_eq!(editor.text(cx), "bcade\n");
 4288        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4289
 4290        editor.transpose(&Default::default(), window, cx);
 4291        assert_eq!(editor.text(cx), "bcda\ne");
 4292        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4293
 4294        editor.transpose(&Default::default(), window, cx);
 4295        assert_eq!(editor.text(cx), "bcade\n");
 4296        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4297
 4298        editor.transpose(&Default::default(), window, cx);
 4299        assert_eq!(editor.text(cx), "bcaed\n");
 4300        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4301
 4302        editor
 4303    });
 4304
 4305    _ = cx.add_window(|window, cx| {
 4306        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4307        editor.set_style(EditorStyle::default(), window, cx);
 4308        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4309        editor.transpose(&Default::default(), window, cx);
 4310        assert_eq!(editor.text(cx), "🏀🍐✋");
 4311        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4312
 4313        editor.transpose(&Default::default(), window, cx);
 4314        assert_eq!(editor.text(cx), "🏀✋🍐");
 4315        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4316
 4317        editor.transpose(&Default::default(), window, cx);
 4318        assert_eq!(editor.text(cx), "🏀🍐✋");
 4319        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4320
 4321        editor
 4322    });
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_rewrap(cx: &mut TestAppContext) {
 4327    init_test(cx, |settings| {
 4328        settings.languages.extend([
 4329            (
 4330                "Markdown".into(),
 4331                LanguageSettingsContent {
 4332                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4333                    ..Default::default()
 4334                },
 4335            ),
 4336            (
 4337                "Plain Text".into(),
 4338                LanguageSettingsContent {
 4339                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4340                    ..Default::default()
 4341                },
 4342            ),
 4343        ])
 4344    });
 4345
 4346    let mut cx = EditorTestContext::new(cx).await;
 4347
 4348    let language_with_c_comments = Arc::new(Language::new(
 4349        LanguageConfig {
 4350            line_comments: vec!["// ".into()],
 4351            ..LanguageConfig::default()
 4352        },
 4353        None,
 4354    ));
 4355    let language_with_pound_comments = Arc::new(Language::new(
 4356        LanguageConfig {
 4357            line_comments: vec!["# ".into()],
 4358            ..LanguageConfig::default()
 4359        },
 4360        None,
 4361    ));
 4362    let markdown_language = Arc::new(Language::new(
 4363        LanguageConfig {
 4364            name: "Markdown".into(),
 4365            ..LanguageConfig::default()
 4366        },
 4367        None,
 4368    ));
 4369    let language_with_doc_comments = Arc::new(Language::new(
 4370        LanguageConfig {
 4371            line_comments: vec!["// ".into(), "/// ".into()],
 4372            ..LanguageConfig::default()
 4373        },
 4374        Some(tree_sitter_rust::LANGUAGE.into()),
 4375    ));
 4376
 4377    let plaintext_language = Arc::new(Language::new(
 4378        LanguageConfig {
 4379            name: "Plain Text".into(),
 4380            ..LanguageConfig::default()
 4381        },
 4382        None,
 4383    ));
 4384
 4385    assert_rewrap(
 4386        indoc! {"
 4387            // ˇ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.
 4388        "},
 4389        indoc! {"
 4390            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4391            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4392            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4393            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4394            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4395            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4396            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4397            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4398            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4399            // porttitor id. Aliquam id accumsan eros.
 4400        "},
 4401        language_with_c_comments.clone(),
 4402        &mut cx,
 4403    );
 4404
 4405    // Test that rewrapping works inside of a selection
 4406    assert_rewrap(
 4407        indoc! {"
 4408            «// 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.ˇ»
 4409        "},
 4410        indoc! {"
 4411            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4412            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4413            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4414            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4415            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4416            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4417            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4418            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4419            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4420            // porttitor id. Aliquam id accumsan eros.ˇ»
 4421        "},
 4422        language_with_c_comments.clone(),
 4423        &mut cx,
 4424    );
 4425
 4426    // Test that cursors that expand to the same region are collapsed.
 4427    assert_rewrap(
 4428        indoc! {"
 4429            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4430            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4431            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4432            // ˇ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.
 4433        "},
 4434        indoc! {"
 4435            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4436            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4437            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4438            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4439            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4440            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4441            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4442            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4443            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4444            // porttitor id. Aliquam id accumsan eros.
 4445        "},
 4446        language_with_c_comments.clone(),
 4447        &mut cx,
 4448    );
 4449
 4450    // Test that non-contiguous selections are treated separately.
 4451    assert_rewrap(
 4452        indoc! {"
 4453            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4454            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4455            //
 4456            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4457            // ˇ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.
 4458        "},
 4459        indoc! {"
 4460            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4461            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4462            // auctor, eu lacinia sapien scelerisque.
 4463            //
 4464            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4465            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4466            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4467            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4468            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4469            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4470            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4471        "},
 4472        language_with_c_comments.clone(),
 4473        &mut cx,
 4474    );
 4475
 4476    // Test that different comment prefixes are supported.
 4477    assert_rewrap(
 4478        indoc! {"
 4479            # ˇ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.
 4480        "},
 4481        indoc! {"
 4482            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4483            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4484            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4485            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4486            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4487            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4488            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4489            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4490            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4491            # accumsan eros.
 4492        "},
 4493        language_with_pound_comments.clone(),
 4494        &mut cx,
 4495    );
 4496
 4497    // Test that rewrapping is ignored outside of comments in most languages.
 4498    assert_rewrap(
 4499        indoc! {"
 4500            /// Adds two numbers.
 4501            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4502            fn add(a: u32, b: u32) -> u32 {
 4503                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ˇ
 4504            }
 4505        "},
 4506        indoc! {"
 4507            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4508            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4509            fn add(a: u32, b: u32) -> u32 {
 4510                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ˇ
 4511            }
 4512        "},
 4513        language_with_doc_comments.clone(),
 4514        &mut cx,
 4515    );
 4516
 4517    // Test that rewrapping works in Markdown and Plain Text languages.
 4518    assert_rewrap(
 4519        indoc! {"
 4520            # Hello
 4521
 4522            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.
 4523        "},
 4524        indoc! {"
 4525            # Hello
 4526
 4527            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4528            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4529            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4530            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4531            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4532            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4533            Integer sit amet scelerisque nisi.
 4534        "},
 4535        markdown_language,
 4536        &mut cx,
 4537    );
 4538
 4539    assert_rewrap(
 4540        indoc! {"
 4541            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.
 4542        "},
 4543        indoc! {"
 4544            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4545            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4546            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4547            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4548            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4549            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4550            Integer sit amet scelerisque nisi.
 4551        "},
 4552        plaintext_language,
 4553        &mut cx,
 4554    );
 4555
 4556    // Test rewrapping unaligned comments in a selection.
 4557    assert_rewrap(
 4558        indoc! {"
 4559            fn foo() {
 4560                if true {
 4561            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4562            // Praesent semper egestas tellus id dignissim.ˇ»
 4563                    do_something();
 4564                } else {
 4565                    //
 4566                }
 4567            }
 4568        "},
 4569        indoc! {"
 4570            fn foo() {
 4571                if true {
 4572            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4573                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4574                    // egestas tellus id dignissim.ˇ»
 4575                    do_something();
 4576                } else {
 4577                    //
 4578                }
 4579            }
 4580        "},
 4581        language_with_doc_comments.clone(),
 4582        &mut cx,
 4583    );
 4584
 4585    assert_rewrap(
 4586        indoc! {"
 4587            fn foo() {
 4588                if true {
 4589            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4590            // Praesent semper egestas tellus id dignissim.»
 4591                    do_something();
 4592                } else {
 4593                    //
 4594                }
 4595
 4596            }
 4597        "},
 4598        indoc! {"
 4599            fn foo() {
 4600                if true {
 4601            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4602                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4603                    // egestas tellus id dignissim.»
 4604                    do_something();
 4605                } else {
 4606                    //
 4607                }
 4608
 4609            }
 4610        "},
 4611        language_with_doc_comments.clone(),
 4612        &mut cx,
 4613    );
 4614
 4615    #[track_caller]
 4616    fn assert_rewrap(
 4617        unwrapped_text: &str,
 4618        wrapped_text: &str,
 4619        language: Arc<Language>,
 4620        cx: &mut EditorTestContext,
 4621    ) {
 4622        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4623        cx.set_state(unwrapped_text);
 4624        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4625        cx.assert_editor_state(wrapped_text);
 4626    }
 4627}
 4628
 4629#[gpui::test]
 4630async fn test_clipboard(cx: &mut TestAppContext) {
 4631    init_test(cx, |_| {});
 4632
 4633    let mut cx = EditorTestContext::new(cx).await;
 4634
 4635    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4636    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4637    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4638
 4639    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4640    cx.set_state("two ˇfour ˇsix ˇ");
 4641    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4642    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4643
 4644    // Paste again but with only two cursors. Since the number of cursors doesn't
 4645    // match the number of slices in the clipboard, the entire clipboard text
 4646    // is pasted at each cursor.
 4647    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4648    cx.update_editor(|e, window, cx| {
 4649        e.handle_input("( ", window, cx);
 4650        e.paste(&Paste, window, cx);
 4651        e.handle_input(") ", window, cx);
 4652    });
 4653    cx.assert_editor_state(
 4654        &([
 4655            "( one✅ ",
 4656            "three ",
 4657            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4658            "three ",
 4659            "five ) ˇ",
 4660        ]
 4661        .join("\n")),
 4662    );
 4663
 4664    // Cut with three selections, one of which is full-line.
 4665    cx.set_state(indoc! {"
 4666        1«2ˇ»3
 4667        4ˇ567
 4668        «8ˇ»9"});
 4669    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4670    cx.assert_editor_state(indoc! {"
 4671        1ˇ3
 4672        ˇ9"});
 4673
 4674    // Paste with three selections, noticing how the copied selection that was full-line
 4675    // gets inserted before the second cursor.
 4676    cx.set_state(indoc! {"
 4677        1ˇ3
 4678 4679        «oˇ»ne"});
 4680    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4681    cx.assert_editor_state(indoc! {"
 4682        12ˇ3
 4683        4567
 4684 4685        8ˇne"});
 4686
 4687    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4688    cx.set_state(indoc! {"
 4689        The quick brown
 4690        fox juˇmps over
 4691        the lazy dog"});
 4692    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4693    assert_eq!(
 4694        cx.read_from_clipboard()
 4695            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4696        Some("fox jumps over\n".to_string())
 4697    );
 4698
 4699    // Paste with three selections, noticing how the copied full-line selection is inserted
 4700    // before the empty selections but replaces the selection that is non-empty.
 4701    cx.set_state(indoc! {"
 4702        Tˇhe quick brown
 4703        «foˇ»x jumps over
 4704        tˇhe lazy dog"});
 4705    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4706    cx.assert_editor_state(indoc! {"
 4707        fox jumps over
 4708        Tˇhe quick brown
 4709        fox jumps over
 4710        ˇx jumps over
 4711        fox jumps over
 4712        tˇhe lazy dog"});
 4713}
 4714
 4715#[gpui::test]
 4716async fn test_paste_multiline(cx: &mut TestAppContext) {
 4717    init_test(cx, |_| {});
 4718
 4719    let mut cx = EditorTestContext::new(cx).await;
 4720    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4721
 4722    // Cut an indented block, without the leading whitespace.
 4723    cx.set_state(indoc! {"
 4724        const a: B = (
 4725            c(),
 4726            «d(
 4727                e,
 4728                f
 4729            )ˇ»
 4730        );
 4731    "});
 4732    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4733    cx.assert_editor_state(indoc! {"
 4734        const a: B = (
 4735            c(),
 4736            ˇ
 4737        );
 4738    "});
 4739
 4740    // Paste it at the same position.
 4741    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4742    cx.assert_editor_state(indoc! {"
 4743        const a: B = (
 4744            c(),
 4745            d(
 4746                e,
 4747                f
 4748 4749        );
 4750    "});
 4751
 4752    // Paste it at a line with a lower indent level.
 4753    cx.set_state(indoc! {"
 4754        ˇ
 4755        const a: B = (
 4756            c(),
 4757        );
 4758    "});
 4759    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4760    cx.assert_editor_state(indoc! {"
 4761        d(
 4762            e,
 4763            f
 4764 4765        const a: B = (
 4766            c(),
 4767        );
 4768    "});
 4769
 4770    // Cut an indented block, with the leading whitespace.
 4771    cx.set_state(indoc! {"
 4772        const a: B = (
 4773            c(),
 4774        «    d(
 4775                e,
 4776                f
 4777            )
 4778        ˇ»);
 4779    "});
 4780    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4781    cx.assert_editor_state(indoc! {"
 4782        const a: B = (
 4783            c(),
 4784        ˇ);
 4785    "});
 4786
 4787    // Paste it at the same position.
 4788    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4789    cx.assert_editor_state(indoc! {"
 4790        const a: B = (
 4791            c(),
 4792            d(
 4793                e,
 4794                f
 4795            )
 4796        ˇ);
 4797    "});
 4798
 4799    // Paste it at a line with a higher indent level.
 4800    cx.set_state(indoc! {"
 4801        const a: B = (
 4802            c(),
 4803            d(
 4804                e,
 4805 4806            )
 4807        );
 4808    "});
 4809    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4810    cx.assert_editor_state(indoc! {"
 4811        const a: B = (
 4812            c(),
 4813            d(
 4814                e,
 4815                f    d(
 4816                    e,
 4817                    f
 4818                )
 4819        ˇ
 4820            )
 4821        );
 4822    "});
 4823}
 4824
 4825#[gpui::test]
 4826async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4827    init_test(cx, |_| {});
 4828
 4829    cx.write_to_clipboard(ClipboardItem::new_string(
 4830        "    d(\n        e\n    );\n".into(),
 4831    ));
 4832
 4833    let mut cx = EditorTestContext::new(cx).await;
 4834    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4835
 4836    cx.set_state(indoc! {"
 4837        fn a() {
 4838            b();
 4839            if c() {
 4840                ˇ
 4841            }
 4842        }
 4843    "});
 4844
 4845    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4846    cx.assert_editor_state(indoc! {"
 4847        fn a() {
 4848            b();
 4849            if c() {
 4850                d(
 4851                    e
 4852                );
 4853        ˇ
 4854            }
 4855        }
 4856    "});
 4857
 4858    cx.set_state(indoc! {"
 4859        fn a() {
 4860            b();
 4861            ˇ
 4862        }
 4863    "});
 4864
 4865    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4866    cx.assert_editor_state(indoc! {"
 4867        fn a() {
 4868            b();
 4869            d(
 4870                e
 4871            );
 4872        ˇ
 4873        }
 4874    "});
 4875}
 4876
 4877#[gpui::test]
 4878fn test_select_all(cx: &mut TestAppContext) {
 4879    init_test(cx, |_| {});
 4880
 4881    let editor = cx.add_window(|window, cx| {
 4882        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4883        build_editor(buffer, window, cx)
 4884    });
 4885    _ = editor.update(cx, |editor, window, cx| {
 4886        editor.select_all(&SelectAll, window, cx);
 4887        assert_eq!(
 4888            editor.selections.display_ranges(cx),
 4889            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4890        );
 4891    });
 4892}
 4893
 4894#[gpui::test]
 4895fn test_select_line(cx: &mut TestAppContext) {
 4896    init_test(cx, |_| {});
 4897
 4898    let editor = cx.add_window(|window, cx| {
 4899        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4900        build_editor(buffer, window, cx)
 4901    });
 4902    _ = editor.update(cx, |editor, window, cx| {
 4903        editor.change_selections(None, window, cx, |s| {
 4904            s.select_display_ranges([
 4905                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4906                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4907                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4908                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4909            ])
 4910        });
 4911        editor.select_line(&SelectLine, window, cx);
 4912        assert_eq!(
 4913            editor.selections.display_ranges(cx),
 4914            vec![
 4915                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4916                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4917            ]
 4918        );
 4919    });
 4920
 4921    _ = editor.update(cx, |editor, window, cx| {
 4922        editor.select_line(&SelectLine, window, cx);
 4923        assert_eq!(
 4924            editor.selections.display_ranges(cx),
 4925            vec![
 4926                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4927                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4928            ]
 4929        );
 4930    });
 4931
 4932    _ = editor.update(cx, |editor, window, cx| {
 4933        editor.select_line(&SelectLine, window, cx);
 4934        assert_eq!(
 4935            editor.selections.display_ranges(cx),
 4936            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4937        );
 4938    });
 4939}
 4940
 4941#[gpui::test]
 4942async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4943    init_test(cx, |_| {});
 4944    let mut cx = EditorTestContext::new(cx).await;
 4945
 4946    #[track_caller]
 4947    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4948        cx.set_state(initial_state);
 4949        cx.update_editor(|e, window, cx| {
 4950            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4951        });
 4952        cx.assert_editor_state(expected_state);
 4953    }
 4954
 4955    // Selection starts and ends at the middle of lines, left-to-right
 4956    test(
 4957        &mut cx,
 4958        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4959        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4960    );
 4961    // Same thing, right-to-left
 4962    test(
 4963        &mut cx,
 4964        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4965        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4966    );
 4967
 4968    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4969    test(
 4970        &mut cx,
 4971        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4972        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4973    );
 4974    // Same thing, right-to-left
 4975    test(
 4976        &mut cx,
 4977        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4978        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4979    );
 4980
 4981    // Whole buffer, left-to-right, last line ends with newline
 4982    test(
 4983        &mut cx,
 4984        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4985        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4986    );
 4987    // Same thing, right-to-left
 4988    test(
 4989        &mut cx,
 4990        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4991        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4992    );
 4993
 4994    // Starts at the end of a line, ends at the start of another
 4995    test(
 4996        &mut cx,
 4997        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4998        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4999    );
 5000}
 5001
 5002#[gpui::test]
 5003async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5004    init_test(cx, |_| {});
 5005
 5006    let editor = cx.add_window(|window, cx| {
 5007        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5008        build_editor(buffer, window, cx)
 5009    });
 5010
 5011    // setup
 5012    _ = editor.update(cx, |editor, window, cx| {
 5013        editor.fold_creases(
 5014            vec![
 5015                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5016                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5017                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5018            ],
 5019            true,
 5020            window,
 5021            cx,
 5022        );
 5023        assert_eq!(
 5024            editor.display_text(cx),
 5025            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5026        );
 5027    });
 5028
 5029    _ = editor.update(cx, |editor, window, cx| {
 5030        editor.change_selections(None, window, cx, |s| {
 5031            s.select_display_ranges([
 5032                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5033                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5034                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5035                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5036            ])
 5037        });
 5038        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5039        assert_eq!(
 5040            editor.display_text(cx),
 5041            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5042        );
 5043    });
 5044    EditorTestContext::for_editor(editor, cx)
 5045        .await
 5046        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5047
 5048    _ = editor.update(cx, |editor, window, cx| {
 5049        editor.change_selections(None, window, cx, |s| {
 5050            s.select_display_ranges([
 5051                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5052            ])
 5053        });
 5054        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5055        assert_eq!(
 5056            editor.display_text(cx),
 5057            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5058        );
 5059        assert_eq!(
 5060            editor.selections.display_ranges(cx),
 5061            [
 5062                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5063                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5064                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5065                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5066                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5067                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5068                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5069            ]
 5070        );
 5071    });
 5072    EditorTestContext::for_editor(editor, cx)
 5073        .await
 5074        .assert_editor_state(
 5075            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5076        );
 5077}
 5078
 5079#[gpui::test]
 5080async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5081    init_test(cx, |_| {});
 5082
 5083    let mut cx = EditorTestContext::new(cx).await;
 5084
 5085    cx.set_state(indoc!(
 5086        r#"abc
 5087           defˇghi
 5088
 5089           jk
 5090           nlmo
 5091           "#
 5092    ));
 5093
 5094    cx.update_editor(|editor, window, cx| {
 5095        editor.add_selection_above(&Default::default(), window, cx);
 5096    });
 5097
 5098    cx.assert_editor_state(indoc!(
 5099        r#"abcˇ
 5100           defˇghi
 5101
 5102           jk
 5103           nlmo
 5104           "#
 5105    ));
 5106
 5107    cx.update_editor(|editor, window, cx| {
 5108        editor.add_selection_above(&Default::default(), window, cx);
 5109    });
 5110
 5111    cx.assert_editor_state(indoc!(
 5112        r#"abcˇ
 5113            defˇghi
 5114
 5115            jk
 5116            nlmo
 5117            "#
 5118    ));
 5119
 5120    cx.update_editor(|editor, window, cx| {
 5121        editor.add_selection_below(&Default::default(), window, cx);
 5122    });
 5123
 5124    cx.assert_editor_state(indoc!(
 5125        r#"abc
 5126           defˇghi
 5127
 5128           jk
 5129           nlmo
 5130           "#
 5131    ));
 5132
 5133    cx.update_editor(|editor, window, cx| {
 5134        editor.undo_selection(&Default::default(), window, cx);
 5135    });
 5136
 5137    cx.assert_editor_state(indoc!(
 5138        r#"abcˇ
 5139           defˇghi
 5140
 5141           jk
 5142           nlmo
 5143           "#
 5144    ));
 5145
 5146    cx.update_editor(|editor, window, cx| {
 5147        editor.redo_selection(&Default::default(), window, cx);
 5148    });
 5149
 5150    cx.assert_editor_state(indoc!(
 5151        r#"abc
 5152           defˇghi
 5153
 5154           jk
 5155           nlmo
 5156           "#
 5157    ));
 5158
 5159    cx.update_editor(|editor, window, cx| {
 5160        editor.add_selection_below(&Default::default(), window, cx);
 5161    });
 5162
 5163    cx.assert_editor_state(indoc!(
 5164        r#"abc
 5165           defˇghi
 5166
 5167           jk
 5168           nlmˇo
 5169           "#
 5170    ));
 5171
 5172    cx.update_editor(|editor, window, cx| {
 5173        editor.add_selection_below(&Default::default(), window, cx);
 5174    });
 5175
 5176    cx.assert_editor_state(indoc!(
 5177        r#"abc
 5178           defˇghi
 5179
 5180           jk
 5181           nlmˇo
 5182           "#
 5183    ));
 5184
 5185    // change selections
 5186    cx.set_state(indoc!(
 5187        r#"abc
 5188           def«ˇg»hi
 5189
 5190           jk
 5191           nlmo
 5192           "#
 5193    ));
 5194
 5195    cx.update_editor(|editor, window, cx| {
 5196        editor.add_selection_below(&Default::default(), window, cx);
 5197    });
 5198
 5199    cx.assert_editor_state(indoc!(
 5200        r#"abc
 5201           def«ˇg»hi
 5202
 5203           jk
 5204           nlm«ˇo»
 5205           "#
 5206    ));
 5207
 5208    cx.update_editor(|editor, window, cx| {
 5209        editor.add_selection_below(&Default::default(), window, cx);
 5210    });
 5211
 5212    cx.assert_editor_state(indoc!(
 5213        r#"abc
 5214           def«ˇg»hi
 5215
 5216           jk
 5217           nlm«ˇo»
 5218           "#
 5219    ));
 5220
 5221    cx.update_editor(|editor, window, cx| {
 5222        editor.add_selection_above(&Default::default(), window, cx);
 5223    });
 5224
 5225    cx.assert_editor_state(indoc!(
 5226        r#"abc
 5227           def«ˇg»hi
 5228
 5229           jk
 5230           nlmo
 5231           "#
 5232    ));
 5233
 5234    cx.update_editor(|editor, window, cx| {
 5235        editor.add_selection_above(&Default::default(), window, cx);
 5236    });
 5237
 5238    cx.assert_editor_state(indoc!(
 5239        r#"abc
 5240           def«ˇg»hi
 5241
 5242           jk
 5243           nlmo
 5244           "#
 5245    ));
 5246
 5247    // Change selections again
 5248    cx.set_state(indoc!(
 5249        r#"a«bc
 5250           defgˇ»hi
 5251
 5252           jk
 5253           nlmo
 5254           "#
 5255    ));
 5256
 5257    cx.update_editor(|editor, window, cx| {
 5258        editor.add_selection_below(&Default::default(), window, cx);
 5259    });
 5260
 5261    cx.assert_editor_state(indoc!(
 5262        r#"a«bcˇ»
 5263           d«efgˇ»hi
 5264
 5265           j«kˇ»
 5266           nlmo
 5267           "#
 5268    ));
 5269
 5270    cx.update_editor(|editor, window, cx| {
 5271        editor.add_selection_below(&Default::default(), window, cx);
 5272    });
 5273    cx.assert_editor_state(indoc!(
 5274        r#"a«bcˇ»
 5275           d«efgˇ»hi
 5276
 5277           j«kˇ»
 5278           n«lmoˇ»
 5279           "#
 5280    ));
 5281    cx.update_editor(|editor, window, cx| {
 5282        editor.add_selection_above(&Default::default(), window, cx);
 5283    });
 5284
 5285    cx.assert_editor_state(indoc!(
 5286        r#"a«bcˇ»
 5287           d«efgˇ»hi
 5288
 5289           j«kˇ»
 5290           nlmo
 5291           "#
 5292    ));
 5293
 5294    // Change selections again
 5295    cx.set_state(indoc!(
 5296        r#"abc
 5297           d«ˇefghi
 5298
 5299           jk
 5300           nlm»o
 5301           "#
 5302    ));
 5303
 5304    cx.update_editor(|editor, window, cx| {
 5305        editor.add_selection_above(&Default::default(), window, cx);
 5306    });
 5307
 5308    cx.assert_editor_state(indoc!(
 5309        r#"a«ˇbc»
 5310           d«ˇef»ghi
 5311
 5312           j«ˇk»
 5313           n«ˇlm»o
 5314           "#
 5315    ));
 5316
 5317    cx.update_editor(|editor, window, cx| {
 5318        editor.add_selection_below(&Default::default(), window, cx);
 5319    });
 5320
 5321    cx.assert_editor_state(indoc!(
 5322        r#"abc
 5323           d«ˇef»ghi
 5324
 5325           j«ˇk»
 5326           n«ˇlm»o
 5327           "#
 5328    ));
 5329}
 5330
 5331#[gpui::test]
 5332async fn test_select_next(cx: &mut TestAppContext) {
 5333    init_test(cx, |_| {});
 5334
 5335    let mut cx = EditorTestContext::new(cx).await;
 5336    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5337
 5338    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5339        .unwrap();
 5340    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5341
 5342    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5343        .unwrap();
 5344    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5345
 5346    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5347    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5348
 5349    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5350    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5351
 5352    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5353        .unwrap();
 5354    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5355
 5356    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5357        .unwrap();
 5358    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5359}
 5360
 5361#[gpui::test]
 5362async fn test_select_all_matches(cx: &mut TestAppContext) {
 5363    init_test(cx, |_| {});
 5364
 5365    let mut cx = EditorTestContext::new(cx).await;
 5366
 5367    // Test caret-only selections
 5368    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5369    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5370        .unwrap();
 5371    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5372
 5373    // Test left-to-right selections
 5374    cx.set_state("abc\n«abcˇ»\nabc");
 5375    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5376        .unwrap();
 5377    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5378
 5379    // Test right-to-left selections
 5380    cx.set_state("abc\n«ˇabc»\nabc");
 5381    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5382        .unwrap();
 5383    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5384
 5385    // Test selecting whitespace with caret selection
 5386    cx.set_state("abc\nˇ   abc\nabc");
 5387    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5388        .unwrap();
 5389    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5390
 5391    // Test selecting whitespace with left-to-right selection
 5392    cx.set_state("abc\n«ˇ  »abc\nabc");
 5393    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5394        .unwrap();
 5395    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5396
 5397    // Test no matches with right-to-left selection
 5398    cx.set_state("abc\n«  ˇ»abc\nabc");
 5399    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5400        .unwrap();
 5401    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5402}
 5403
 5404#[gpui::test]
 5405async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5406    init_test(cx, |_| {});
 5407
 5408    let mut cx = EditorTestContext::new(cx).await;
 5409    cx.set_state(
 5410        r#"let foo = 2;
 5411lˇet foo = 2;
 5412let fooˇ = 2;
 5413let foo = 2;
 5414let foo = ˇ2;"#,
 5415    );
 5416
 5417    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5418        .unwrap();
 5419    cx.assert_editor_state(
 5420        r#"let foo = 2;
 5421«letˇ» foo = 2;
 5422let «fooˇ» = 2;
 5423let foo = 2;
 5424let foo = «2ˇ»;"#,
 5425    );
 5426
 5427    // noop for multiple selections with different contents
 5428    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5429        .unwrap();
 5430    cx.assert_editor_state(
 5431        r#"let foo = 2;
 5432«letˇ» foo = 2;
 5433let «fooˇ» = 2;
 5434let foo = 2;
 5435let foo = «2ˇ»;"#,
 5436    );
 5437}
 5438
 5439#[gpui::test]
 5440async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5441    init_test(cx, |_| {});
 5442
 5443    let mut cx =
 5444        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5445
 5446    cx.assert_editor_state(indoc! {"
 5447        ˇbbb
 5448        ccc
 5449
 5450        bbb
 5451        ccc
 5452        "});
 5453    cx.dispatch_action(SelectPrevious::default());
 5454    cx.assert_editor_state(indoc! {"
 5455                «bbbˇ»
 5456                ccc
 5457
 5458                bbb
 5459                ccc
 5460                "});
 5461    cx.dispatch_action(SelectPrevious::default());
 5462    cx.assert_editor_state(indoc! {"
 5463                «bbbˇ»
 5464                ccc
 5465
 5466                «bbbˇ»
 5467                ccc
 5468                "});
 5469}
 5470
 5471#[gpui::test]
 5472async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5473    init_test(cx, |_| {});
 5474
 5475    let mut cx = EditorTestContext::new(cx).await;
 5476    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5477
 5478    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5479        .unwrap();
 5480    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5481
 5482    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5483        .unwrap();
 5484    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5485
 5486    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5487    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5488
 5489    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5490    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5491
 5492    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5493        .unwrap();
 5494    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5495
 5496    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5497        .unwrap();
 5498    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5499
 5500    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5501        .unwrap();
 5502    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5503}
 5504
 5505#[gpui::test]
 5506async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5507    init_test(cx, |_| {});
 5508
 5509    let mut cx = EditorTestContext::new(cx).await;
 5510    cx.set_state("");
 5511
 5512    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5513        .unwrap();
 5514    cx.assert_editor_state("«aˇ»");
 5515    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5516        .unwrap();
 5517    cx.assert_editor_state("«aˇ»");
 5518}
 5519
 5520#[gpui::test]
 5521async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5522    init_test(cx, |_| {});
 5523
 5524    let mut cx = EditorTestContext::new(cx).await;
 5525    cx.set_state(
 5526        r#"let foo = 2;
 5527lˇet foo = 2;
 5528let fooˇ = 2;
 5529let foo = 2;
 5530let foo = ˇ2;"#,
 5531    );
 5532
 5533    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5534        .unwrap();
 5535    cx.assert_editor_state(
 5536        r#"let foo = 2;
 5537«letˇ» foo = 2;
 5538let «fooˇ» = 2;
 5539let foo = 2;
 5540let foo = «2ˇ»;"#,
 5541    );
 5542
 5543    // noop for multiple selections with different contents
 5544    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5545        .unwrap();
 5546    cx.assert_editor_state(
 5547        r#"let foo = 2;
 5548«letˇ» foo = 2;
 5549let «fooˇ» = 2;
 5550let foo = 2;
 5551let foo = «2ˇ»;"#,
 5552    );
 5553}
 5554
 5555#[gpui::test]
 5556async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5557    init_test(cx, |_| {});
 5558
 5559    let mut cx = EditorTestContext::new(cx).await;
 5560    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5561
 5562    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5563        .unwrap();
 5564    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5565
 5566    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5567        .unwrap();
 5568    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5569
 5570    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5571    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5572
 5573    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5574    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5575
 5576    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5577        .unwrap();
 5578    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5579
 5580    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5581        .unwrap();
 5582    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5583}
 5584
 5585#[gpui::test]
 5586async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5587    init_test(cx, |_| {});
 5588
 5589    let language = Arc::new(Language::new(
 5590        LanguageConfig::default(),
 5591        Some(tree_sitter_rust::LANGUAGE.into()),
 5592    ));
 5593
 5594    let text = r#"
 5595        use mod1::mod2::{mod3, mod4};
 5596
 5597        fn fn_1(param1: bool, param2: &str) {
 5598            let var1 = "text";
 5599        }
 5600    "#
 5601    .unindent();
 5602
 5603    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5604    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5605    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5606
 5607    editor
 5608        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5609        .await;
 5610
 5611    editor.update_in(cx, |editor, window, cx| {
 5612        editor.change_selections(None, window, cx, |s| {
 5613            s.select_display_ranges([
 5614                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5615                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5616                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5617            ]);
 5618        });
 5619        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5620    });
 5621    editor.update(cx, |editor, cx| {
 5622        assert_text_with_selections(
 5623            editor,
 5624            indoc! {r#"
 5625                use mod1::mod2::{mod3, «mod4ˇ»};
 5626
 5627                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5628                    let var1 = "«textˇ»";
 5629                }
 5630            "#},
 5631            cx,
 5632        );
 5633    });
 5634
 5635    editor.update_in(cx, |editor, window, cx| {
 5636        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5637    });
 5638    editor.update(cx, |editor, cx| {
 5639        assert_text_with_selections(
 5640            editor,
 5641            indoc! {r#"
 5642                use mod1::mod2::«{mod3, mod4}ˇ»;
 5643
 5644                «ˇfn fn_1(param1: bool, param2: &str) {
 5645                    let var1 = "text";
 5646 5647            "#},
 5648            cx,
 5649        );
 5650    });
 5651
 5652    editor.update_in(cx, |editor, window, cx| {
 5653        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5654    });
 5655    assert_eq!(
 5656        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5657        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5658    );
 5659
 5660    // Trying to expand the selected syntax node one more time has no effect.
 5661    editor.update_in(cx, |editor, window, cx| {
 5662        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5663    });
 5664    assert_eq!(
 5665        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5666        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5667    );
 5668
 5669    editor.update_in(cx, |editor, window, cx| {
 5670        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5671    });
 5672    editor.update(cx, |editor, cx| {
 5673        assert_text_with_selections(
 5674            editor,
 5675            indoc! {r#"
 5676                use mod1::mod2::«{mod3, mod4}ˇ»;
 5677
 5678                «ˇfn fn_1(param1: bool, param2: &str) {
 5679                    let var1 = "text";
 5680 5681            "#},
 5682            cx,
 5683        );
 5684    });
 5685
 5686    editor.update_in(cx, |editor, window, cx| {
 5687        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5688    });
 5689    editor.update(cx, |editor, cx| {
 5690        assert_text_with_selections(
 5691            editor,
 5692            indoc! {r#"
 5693                use mod1::mod2::{mod3, «mod4ˇ»};
 5694
 5695                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5696                    let var1 = "«textˇ»";
 5697                }
 5698            "#},
 5699            cx,
 5700        );
 5701    });
 5702
 5703    editor.update_in(cx, |editor, window, cx| {
 5704        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5705    });
 5706    editor.update(cx, |editor, cx| {
 5707        assert_text_with_selections(
 5708            editor,
 5709            indoc! {r#"
 5710                use mod1::mod2::{mod3, mo«ˇ»d4};
 5711
 5712                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5713                    let var1 = "te«ˇ»xt";
 5714                }
 5715            "#},
 5716            cx,
 5717        );
 5718    });
 5719
 5720    // Trying to shrink the selected syntax node one more time has no effect.
 5721    editor.update_in(cx, |editor, window, cx| {
 5722        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5723    });
 5724    editor.update_in(cx, |editor, _, cx| {
 5725        assert_text_with_selections(
 5726            editor,
 5727            indoc! {r#"
 5728                use mod1::mod2::{mod3, mo«ˇ»d4};
 5729
 5730                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5731                    let var1 = "te«ˇ»xt";
 5732                }
 5733            "#},
 5734            cx,
 5735        );
 5736    });
 5737
 5738    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5739    // a fold.
 5740    editor.update_in(cx, |editor, window, cx| {
 5741        editor.fold_creases(
 5742            vec![
 5743                Crease::simple(
 5744                    Point::new(0, 21)..Point::new(0, 24),
 5745                    FoldPlaceholder::test(),
 5746                ),
 5747                Crease::simple(
 5748                    Point::new(3, 20)..Point::new(3, 22),
 5749                    FoldPlaceholder::test(),
 5750                ),
 5751            ],
 5752            true,
 5753            window,
 5754            cx,
 5755        );
 5756        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5757    });
 5758    editor.update(cx, |editor, cx| {
 5759        assert_text_with_selections(
 5760            editor,
 5761            indoc! {r#"
 5762                use mod1::mod2::«{mod3, mod4}ˇ»;
 5763
 5764                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5765                    «let var1 = "text";ˇ»
 5766                }
 5767            "#},
 5768            cx,
 5769        );
 5770    });
 5771}
 5772
 5773#[gpui::test]
 5774async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5775    init_test(cx, |_| {});
 5776
 5777    let base_text = r#"
 5778        impl A {
 5779            // this is an uncommitted comment
 5780
 5781            fn b() {
 5782                c();
 5783            }
 5784
 5785            // this is another uncommitted comment
 5786
 5787            fn d() {
 5788                // e
 5789                // f
 5790            }
 5791        }
 5792
 5793        fn g() {
 5794            // h
 5795        }
 5796    "#
 5797    .unindent();
 5798
 5799    let text = r#"
 5800        ˇimpl A {
 5801
 5802            fn b() {
 5803                c();
 5804            }
 5805
 5806            fn d() {
 5807                // e
 5808                // f
 5809            }
 5810        }
 5811
 5812        fn g() {
 5813            // h
 5814        }
 5815    "#
 5816    .unindent();
 5817
 5818    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5819    cx.set_state(&text);
 5820    cx.set_head_text(&base_text);
 5821    cx.update_editor(|editor, window, cx| {
 5822        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5823    });
 5824
 5825    cx.assert_state_with_diff(
 5826        "
 5827        ˇimpl A {
 5828      -     // this is an uncommitted comment
 5829
 5830            fn b() {
 5831                c();
 5832            }
 5833
 5834      -     // this is another uncommitted comment
 5835      -
 5836            fn d() {
 5837                // e
 5838                // f
 5839            }
 5840        }
 5841
 5842        fn g() {
 5843            // h
 5844        }
 5845    "
 5846        .unindent(),
 5847    );
 5848
 5849    let expected_display_text = "
 5850        impl A {
 5851            // this is an uncommitted comment
 5852
 5853            fn b() {
 5854 5855            }
 5856
 5857            // this is another uncommitted comment
 5858
 5859            fn d() {
 5860 5861            }
 5862        }
 5863
 5864        fn g() {
 5865 5866        }
 5867        "
 5868    .unindent();
 5869
 5870    cx.update_editor(|editor, window, cx| {
 5871        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5872        assert_eq!(editor.display_text(cx), expected_display_text);
 5873    });
 5874}
 5875
 5876#[gpui::test]
 5877async fn test_autoindent(cx: &mut TestAppContext) {
 5878    init_test(cx, |_| {});
 5879
 5880    let language = Arc::new(
 5881        Language::new(
 5882            LanguageConfig {
 5883                brackets: BracketPairConfig {
 5884                    pairs: vec![
 5885                        BracketPair {
 5886                            start: "{".to_string(),
 5887                            end: "}".to_string(),
 5888                            close: false,
 5889                            surround: false,
 5890                            newline: true,
 5891                        },
 5892                        BracketPair {
 5893                            start: "(".to_string(),
 5894                            end: ")".to_string(),
 5895                            close: false,
 5896                            surround: false,
 5897                            newline: true,
 5898                        },
 5899                    ],
 5900                    ..Default::default()
 5901                },
 5902                ..Default::default()
 5903            },
 5904            Some(tree_sitter_rust::LANGUAGE.into()),
 5905        )
 5906        .with_indents_query(
 5907            r#"
 5908                (_ "(" ")" @end) @indent
 5909                (_ "{" "}" @end) @indent
 5910            "#,
 5911        )
 5912        .unwrap(),
 5913    );
 5914
 5915    let text = "fn a() {}";
 5916
 5917    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5918    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5919    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5920    editor
 5921        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5922        .await;
 5923
 5924    editor.update_in(cx, |editor, window, cx| {
 5925        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5926        editor.newline(&Newline, window, cx);
 5927        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5928        assert_eq!(
 5929            editor.selections.ranges(cx),
 5930            &[
 5931                Point::new(1, 4)..Point::new(1, 4),
 5932                Point::new(3, 4)..Point::new(3, 4),
 5933                Point::new(5, 0)..Point::new(5, 0)
 5934            ]
 5935        );
 5936    });
 5937}
 5938
 5939#[gpui::test]
 5940async fn test_autoindent_selections(cx: &mut TestAppContext) {
 5941    init_test(cx, |_| {});
 5942
 5943    {
 5944        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5945        cx.set_state(indoc! {"
 5946            impl A {
 5947
 5948                fn b() {}
 5949
 5950            «fn c() {
 5951
 5952            }ˇ»
 5953            }
 5954        "});
 5955
 5956        cx.update_editor(|editor, window, cx| {
 5957            editor.autoindent(&Default::default(), window, cx);
 5958        });
 5959
 5960        cx.assert_editor_state(indoc! {"
 5961            impl A {
 5962
 5963                fn b() {}
 5964
 5965                «fn c() {
 5966
 5967                }ˇ»
 5968            }
 5969        "});
 5970    }
 5971
 5972    {
 5973        let mut cx = EditorTestContext::new_multibuffer(
 5974            cx,
 5975            [indoc! { "
 5976                impl A {
 5977                «
 5978                // a
 5979                fn b(){}
 5980                »
 5981                «
 5982                    }
 5983                    fn c(){}
 5984                »
 5985            "}],
 5986        );
 5987
 5988        let buffer = cx.update_editor(|editor, _, cx| {
 5989            let buffer = editor.buffer().update(cx, |buffer, _| {
 5990                buffer.all_buffers().iter().next().unwrap().clone()
 5991            });
 5992            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5993            buffer
 5994        });
 5995
 5996        cx.run_until_parked();
 5997        cx.update_editor(|editor, window, cx| {
 5998            editor.select_all(&Default::default(), window, cx);
 5999            editor.autoindent(&Default::default(), window, cx)
 6000        });
 6001        cx.run_until_parked();
 6002
 6003        cx.update(|_, cx| {
 6004            pretty_assertions::assert_eq!(
 6005                buffer.read(cx).text(),
 6006                indoc! { "
 6007                    impl A {
 6008
 6009                        // a
 6010                        fn b(){}
 6011
 6012
 6013                    }
 6014                    fn c(){}
 6015
 6016                " }
 6017            )
 6018        });
 6019    }
 6020}
 6021
 6022#[gpui::test]
 6023async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6024    init_test(cx, |_| {});
 6025
 6026    let mut cx = EditorTestContext::new(cx).await;
 6027
 6028    let language = Arc::new(Language::new(
 6029        LanguageConfig {
 6030            brackets: BracketPairConfig {
 6031                pairs: vec![
 6032                    BracketPair {
 6033                        start: "{".to_string(),
 6034                        end: "}".to_string(),
 6035                        close: true,
 6036                        surround: true,
 6037                        newline: true,
 6038                    },
 6039                    BracketPair {
 6040                        start: "(".to_string(),
 6041                        end: ")".to_string(),
 6042                        close: true,
 6043                        surround: true,
 6044                        newline: true,
 6045                    },
 6046                    BracketPair {
 6047                        start: "/*".to_string(),
 6048                        end: " */".to_string(),
 6049                        close: true,
 6050                        surround: true,
 6051                        newline: true,
 6052                    },
 6053                    BracketPair {
 6054                        start: "[".to_string(),
 6055                        end: "]".to_string(),
 6056                        close: false,
 6057                        surround: false,
 6058                        newline: true,
 6059                    },
 6060                    BracketPair {
 6061                        start: "\"".to_string(),
 6062                        end: "\"".to_string(),
 6063                        close: true,
 6064                        surround: true,
 6065                        newline: false,
 6066                    },
 6067                    BracketPair {
 6068                        start: "<".to_string(),
 6069                        end: ">".to_string(),
 6070                        close: false,
 6071                        surround: true,
 6072                        newline: true,
 6073                    },
 6074                ],
 6075                ..Default::default()
 6076            },
 6077            autoclose_before: "})]".to_string(),
 6078            ..Default::default()
 6079        },
 6080        Some(tree_sitter_rust::LANGUAGE.into()),
 6081    ));
 6082
 6083    cx.language_registry().add(language.clone());
 6084    cx.update_buffer(|buffer, cx| {
 6085        buffer.set_language(Some(language), cx);
 6086    });
 6087
 6088    cx.set_state(
 6089        &r#"
 6090            🏀ˇ
 6091            εˇ
 6092            ❤️ˇ
 6093        "#
 6094        .unindent(),
 6095    );
 6096
 6097    // autoclose multiple nested brackets at multiple cursors
 6098    cx.update_editor(|editor, window, cx| {
 6099        editor.handle_input("{", window, cx);
 6100        editor.handle_input("{", window, cx);
 6101        editor.handle_input("{", window, cx);
 6102    });
 6103    cx.assert_editor_state(
 6104        &"
 6105            🏀{{{ˇ}}}
 6106            ε{{{ˇ}}}
 6107            ❤️{{{ˇ}}}
 6108        "
 6109        .unindent(),
 6110    );
 6111
 6112    // insert a different closing bracket
 6113    cx.update_editor(|editor, window, cx| {
 6114        editor.handle_input(")", window, cx);
 6115    });
 6116    cx.assert_editor_state(
 6117        &"
 6118            🏀{{{)ˇ}}}
 6119            ε{{{)ˇ}}}
 6120            ❤️{{{)ˇ}}}
 6121        "
 6122        .unindent(),
 6123    );
 6124
 6125    // skip over the auto-closed brackets when typing a closing bracket
 6126    cx.update_editor(|editor, window, cx| {
 6127        editor.move_right(&MoveRight, window, cx);
 6128        editor.handle_input("}", window, cx);
 6129        editor.handle_input("}", window, cx);
 6130        editor.handle_input("}", window, cx);
 6131    });
 6132    cx.assert_editor_state(
 6133        &"
 6134            🏀{{{)}}}}ˇ
 6135            ε{{{)}}}}ˇ
 6136            ❤️{{{)}}}}ˇ
 6137        "
 6138        .unindent(),
 6139    );
 6140
 6141    // autoclose multi-character pairs
 6142    cx.set_state(
 6143        &"
 6144            ˇ
 6145            ˇ
 6146        "
 6147        .unindent(),
 6148    );
 6149    cx.update_editor(|editor, window, cx| {
 6150        editor.handle_input("/", window, cx);
 6151        editor.handle_input("*", window, cx);
 6152    });
 6153    cx.assert_editor_state(
 6154        &"
 6155            /*ˇ */
 6156            /*ˇ */
 6157        "
 6158        .unindent(),
 6159    );
 6160
 6161    // one cursor autocloses a multi-character pair, one cursor
 6162    // does not autoclose.
 6163    cx.set_state(
 6164        &"
 6165 6166            ˇ
 6167        "
 6168        .unindent(),
 6169    );
 6170    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6171    cx.assert_editor_state(
 6172        &"
 6173            /*ˇ */
 6174 6175        "
 6176        .unindent(),
 6177    );
 6178
 6179    // Don't autoclose if the next character isn't whitespace and isn't
 6180    // listed in the language's "autoclose_before" section.
 6181    cx.set_state("ˇa b");
 6182    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6183    cx.assert_editor_state("{ˇa b");
 6184
 6185    // Don't autoclose if `close` is false for the bracket pair
 6186    cx.set_state("ˇ");
 6187    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6188    cx.assert_editor_state("");
 6189
 6190    // Surround with brackets if text is selected
 6191    cx.set_state("«aˇ» b");
 6192    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6193    cx.assert_editor_state("{«aˇ»} b");
 6194
 6195    // Autclose pair where the start and end characters are the same
 6196    cx.set_state("");
 6197    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6198    cx.assert_editor_state("a\"ˇ\"");
 6199    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6200    cx.assert_editor_state("a\"\"ˇ");
 6201
 6202    // Don't autoclose pair if autoclose is disabled
 6203    cx.set_state("ˇ");
 6204    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6205    cx.assert_editor_state("");
 6206
 6207    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6208    cx.set_state("«aˇ» b");
 6209    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6210    cx.assert_editor_state("<«aˇ»> b");
 6211}
 6212
 6213#[gpui::test]
 6214async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6215    init_test(cx, |settings| {
 6216        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6217    });
 6218
 6219    let mut cx = EditorTestContext::new(cx).await;
 6220
 6221    let language = Arc::new(Language::new(
 6222        LanguageConfig {
 6223            brackets: BracketPairConfig {
 6224                pairs: vec![
 6225                    BracketPair {
 6226                        start: "{".to_string(),
 6227                        end: "}".to_string(),
 6228                        close: true,
 6229                        surround: true,
 6230                        newline: true,
 6231                    },
 6232                    BracketPair {
 6233                        start: "(".to_string(),
 6234                        end: ")".to_string(),
 6235                        close: true,
 6236                        surround: true,
 6237                        newline: true,
 6238                    },
 6239                    BracketPair {
 6240                        start: "[".to_string(),
 6241                        end: "]".to_string(),
 6242                        close: false,
 6243                        surround: false,
 6244                        newline: true,
 6245                    },
 6246                ],
 6247                ..Default::default()
 6248            },
 6249            autoclose_before: "})]".to_string(),
 6250            ..Default::default()
 6251        },
 6252        Some(tree_sitter_rust::LANGUAGE.into()),
 6253    ));
 6254
 6255    cx.language_registry().add(language.clone());
 6256    cx.update_buffer(|buffer, cx| {
 6257        buffer.set_language(Some(language), cx);
 6258    });
 6259
 6260    cx.set_state(
 6261        &"
 6262            ˇ
 6263            ˇ
 6264            ˇ
 6265        "
 6266        .unindent(),
 6267    );
 6268
 6269    // ensure only matching closing brackets are skipped over
 6270    cx.update_editor(|editor, window, cx| {
 6271        editor.handle_input("}", window, cx);
 6272        editor.move_left(&MoveLeft, window, cx);
 6273        editor.handle_input(")", window, cx);
 6274        editor.move_left(&MoveLeft, window, cx);
 6275    });
 6276    cx.assert_editor_state(
 6277        &"
 6278            ˇ)}
 6279            ˇ)}
 6280            ˇ)}
 6281        "
 6282        .unindent(),
 6283    );
 6284
 6285    // skip-over closing brackets at multiple cursors
 6286    cx.update_editor(|editor, window, cx| {
 6287        editor.handle_input(")", window, cx);
 6288        editor.handle_input("}", window, cx);
 6289    });
 6290    cx.assert_editor_state(
 6291        &"
 6292            )}ˇ
 6293            )}ˇ
 6294            )}ˇ
 6295        "
 6296        .unindent(),
 6297    );
 6298
 6299    // ignore non-close brackets
 6300    cx.update_editor(|editor, window, cx| {
 6301        editor.handle_input("]", window, cx);
 6302        editor.move_left(&MoveLeft, window, cx);
 6303        editor.handle_input("]", window, cx);
 6304    });
 6305    cx.assert_editor_state(
 6306        &"
 6307            )}]ˇ]
 6308            )}]ˇ]
 6309            )}]ˇ]
 6310        "
 6311        .unindent(),
 6312    );
 6313}
 6314
 6315#[gpui::test]
 6316async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6317    init_test(cx, |_| {});
 6318
 6319    let mut cx = EditorTestContext::new(cx).await;
 6320
 6321    let html_language = Arc::new(
 6322        Language::new(
 6323            LanguageConfig {
 6324                name: "HTML".into(),
 6325                brackets: BracketPairConfig {
 6326                    pairs: vec![
 6327                        BracketPair {
 6328                            start: "<".into(),
 6329                            end: ">".into(),
 6330                            close: true,
 6331                            ..Default::default()
 6332                        },
 6333                        BracketPair {
 6334                            start: "{".into(),
 6335                            end: "}".into(),
 6336                            close: true,
 6337                            ..Default::default()
 6338                        },
 6339                        BracketPair {
 6340                            start: "(".into(),
 6341                            end: ")".into(),
 6342                            close: true,
 6343                            ..Default::default()
 6344                        },
 6345                    ],
 6346                    ..Default::default()
 6347                },
 6348                autoclose_before: "})]>".into(),
 6349                ..Default::default()
 6350            },
 6351            Some(tree_sitter_html::LANGUAGE.into()),
 6352        )
 6353        .with_injection_query(
 6354            r#"
 6355            (script_element
 6356                (raw_text) @injection.content
 6357                (#set! injection.language "javascript"))
 6358            "#,
 6359        )
 6360        .unwrap(),
 6361    );
 6362
 6363    let javascript_language = Arc::new(Language::new(
 6364        LanguageConfig {
 6365            name: "JavaScript".into(),
 6366            brackets: BracketPairConfig {
 6367                pairs: vec![
 6368                    BracketPair {
 6369                        start: "/*".into(),
 6370                        end: " */".into(),
 6371                        close: true,
 6372                        ..Default::default()
 6373                    },
 6374                    BracketPair {
 6375                        start: "{".into(),
 6376                        end: "}".into(),
 6377                        close: true,
 6378                        ..Default::default()
 6379                    },
 6380                    BracketPair {
 6381                        start: "(".into(),
 6382                        end: ")".into(),
 6383                        close: true,
 6384                        ..Default::default()
 6385                    },
 6386                ],
 6387                ..Default::default()
 6388            },
 6389            autoclose_before: "})]>".into(),
 6390            ..Default::default()
 6391        },
 6392        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6393    ));
 6394
 6395    cx.language_registry().add(html_language.clone());
 6396    cx.language_registry().add(javascript_language.clone());
 6397
 6398    cx.update_buffer(|buffer, cx| {
 6399        buffer.set_language(Some(html_language), cx);
 6400    });
 6401
 6402    cx.set_state(
 6403        &r#"
 6404            <body>ˇ
 6405                <script>
 6406                    var x = 1;ˇ
 6407                </script>
 6408            </body>ˇ
 6409        "#
 6410        .unindent(),
 6411    );
 6412
 6413    // Precondition: different languages are active at different locations.
 6414    cx.update_editor(|editor, window, cx| {
 6415        let snapshot = editor.snapshot(window, cx);
 6416        let cursors = editor.selections.ranges::<usize>(cx);
 6417        let languages = cursors
 6418            .iter()
 6419            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6420            .collect::<Vec<_>>();
 6421        assert_eq!(
 6422            languages,
 6423            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6424        );
 6425    });
 6426
 6427    // Angle brackets autoclose in HTML, but not JavaScript.
 6428    cx.update_editor(|editor, window, cx| {
 6429        editor.handle_input("<", window, cx);
 6430        editor.handle_input("a", window, cx);
 6431    });
 6432    cx.assert_editor_state(
 6433        &r#"
 6434            <body><aˇ>
 6435                <script>
 6436                    var x = 1;<aˇ
 6437                </script>
 6438            </body><aˇ>
 6439        "#
 6440        .unindent(),
 6441    );
 6442
 6443    // Curly braces and parens autoclose in both HTML and JavaScript.
 6444    cx.update_editor(|editor, window, cx| {
 6445        editor.handle_input(" b=", window, cx);
 6446        editor.handle_input("{", window, cx);
 6447        editor.handle_input("c", window, cx);
 6448        editor.handle_input("(", window, cx);
 6449    });
 6450    cx.assert_editor_state(
 6451        &r#"
 6452            <body><a b={c(ˇ)}>
 6453                <script>
 6454                    var x = 1;<a b={c(ˇ)}
 6455                </script>
 6456            </body><a b={c(ˇ)}>
 6457        "#
 6458        .unindent(),
 6459    );
 6460
 6461    // Brackets that were already autoclosed are skipped.
 6462    cx.update_editor(|editor, window, cx| {
 6463        editor.handle_input(")", window, cx);
 6464        editor.handle_input("d", window, cx);
 6465        editor.handle_input("}", window, cx);
 6466    });
 6467    cx.assert_editor_state(
 6468        &r#"
 6469            <body><a b={c()d}ˇ>
 6470                <script>
 6471                    var x = 1;<a b={c()d}ˇ
 6472                </script>
 6473            </body><a b={c()d}ˇ>
 6474        "#
 6475        .unindent(),
 6476    );
 6477    cx.update_editor(|editor, window, cx| {
 6478        editor.handle_input(">", window, cx);
 6479    });
 6480    cx.assert_editor_state(
 6481        &r#"
 6482            <body><a b={c()d}>ˇ
 6483                <script>
 6484                    var x = 1;<a b={c()d}>ˇ
 6485                </script>
 6486            </body><a b={c()d}>ˇ
 6487        "#
 6488        .unindent(),
 6489    );
 6490
 6491    // Reset
 6492    cx.set_state(
 6493        &r#"
 6494            <body>ˇ
 6495                <script>
 6496                    var x = 1;ˇ
 6497                </script>
 6498            </body>ˇ
 6499        "#
 6500        .unindent(),
 6501    );
 6502
 6503    cx.update_editor(|editor, window, cx| {
 6504        editor.handle_input("<", window, cx);
 6505    });
 6506    cx.assert_editor_state(
 6507        &r#"
 6508            <body><ˇ>
 6509                <script>
 6510                    var x = 1;<ˇ
 6511                </script>
 6512            </body><ˇ>
 6513        "#
 6514        .unindent(),
 6515    );
 6516
 6517    // When backspacing, the closing angle brackets are removed.
 6518    cx.update_editor(|editor, window, cx| {
 6519        editor.backspace(&Backspace, window, cx);
 6520    });
 6521    cx.assert_editor_state(
 6522        &r#"
 6523            <body>ˇ
 6524                <script>
 6525                    var x = 1;ˇ
 6526                </script>
 6527            </body>ˇ
 6528        "#
 6529        .unindent(),
 6530    );
 6531
 6532    // Block comments autoclose in JavaScript, but not HTML.
 6533    cx.update_editor(|editor, window, cx| {
 6534        editor.handle_input("/", window, cx);
 6535        editor.handle_input("*", window, cx);
 6536    });
 6537    cx.assert_editor_state(
 6538        &r#"
 6539            <body>/*ˇ
 6540                <script>
 6541                    var x = 1;/*ˇ */
 6542                </script>
 6543            </body>/*ˇ
 6544        "#
 6545        .unindent(),
 6546    );
 6547}
 6548
 6549#[gpui::test]
 6550async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6551    init_test(cx, |_| {});
 6552
 6553    let mut cx = EditorTestContext::new(cx).await;
 6554
 6555    let rust_language = Arc::new(
 6556        Language::new(
 6557            LanguageConfig {
 6558                name: "Rust".into(),
 6559                brackets: serde_json::from_value(json!([
 6560                    { "start": "{", "end": "}", "close": true, "newline": true },
 6561                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6562                ]))
 6563                .unwrap(),
 6564                autoclose_before: "})]>".into(),
 6565                ..Default::default()
 6566            },
 6567            Some(tree_sitter_rust::LANGUAGE.into()),
 6568        )
 6569        .with_override_query("(string_literal) @string")
 6570        .unwrap(),
 6571    );
 6572
 6573    cx.language_registry().add(rust_language.clone());
 6574    cx.update_buffer(|buffer, cx| {
 6575        buffer.set_language(Some(rust_language), cx);
 6576    });
 6577
 6578    cx.set_state(
 6579        &r#"
 6580            let x = ˇ
 6581        "#
 6582        .unindent(),
 6583    );
 6584
 6585    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6586    cx.update_editor(|editor, window, cx| {
 6587        editor.handle_input("\"", window, cx);
 6588    });
 6589    cx.assert_editor_state(
 6590        &r#"
 6591            let x = "ˇ"
 6592        "#
 6593        .unindent(),
 6594    );
 6595
 6596    // Inserting another quotation mark. The cursor moves across the existing
 6597    // automatically-inserted quotation mark.
 6598    cx.update_editor(|editor, window, cx| {
 6599        editor.handle_input("\"", window, cx);
 6600    });
 6601    cx.assert_editor_state(
 6602        &r#"
 6603            let x = ""ˇ
 6604        "#
 6605        .unindent(),
 6606    );
 6607
 6608    // Reset
 6609    cx.set_state(
 6610        &r#"
 6611            let x = ˇ
 6612        "#
 6613        .unindent(),
 6614    );
 6615
 6616    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6617    cx.update_editor(|editor, window, cx| {
 6618        editor.handle_input("\"", window, cx);
 6619        editor.handle_input(" ", window, cx);
 6620        editor.move_left(&Default::default(), window, cx);
 6621        editor.handle_input("\\", window, cx);
 6622        editor.handle_input("\"", window, cx);
 6623    });
 6624    cx.assert_editor_state(
 6625        &r#"
 6626            let x = "\"ˇ "
 6627        "#
 6628        .unindent(),
 6629    );
 6630
 6631    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6632    // mark. Nothing is inserted.
 6633    cx.update_editor(|editor, window, cx| {
 6634        editor.move_right(&Default::default(), window, cx);
 6635        editor.handle_input("\"", window, cx);
 6636    });
 6637    cx.assert_editor_state(
 6638        &r#"
 6639            let x = "\" "ˇ
 6640        "#
 6641        .unindent(),
 6642    );
 6643}
 6644
 6645#[gpui::test]
 6646async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6647    init_test(cx, |_| {});
 6648
 6649    let language = Arc::new(Language::new(
 6650        LanguageConfig {
 6651            brackets: BracketPairConfig {
 6652                pairs: vec![
 6653                    BracketPair {
 6654                        start: "{".to_string(),
 6655                        end: "}".to_string(),
 6656                        close: true,
 6657                        surround: true,
 6658                        newline: true,
 6659                    },
 6660                    BracketPair {
 6661                        start: "/* ".to_string(),
 6662                        end: "*/".to_string(),
 6663                        close: true,
 6664                        surround: true,
 6665                        ..Default::default()
 6666                    },
 6667                ],
 6668                ..Default::default()
 6669            },
 6670            ..Default::default()
 6671        },
 6672        Some(tree_sitter_rust::LANGUAGE.into()),
 6673    ));
 6674
 6675    let text = r#"
 6676        a
 6677        b
 6678        c
 6679    "#
 6680    .unindent();
 6681
 6682    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6683    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6684    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6685    editor
 6686        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6687        .await;
 6688
 6689    editor.update_in(cx, |editor, window, cx| {
 6690        editor.change_selections(None, window, cx, |s| {
 6691            s.select_display_ranges([
 6692                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6693                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6694                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6695            ])
 6696        });
 6697
 6698        editor.handle_input("{", window, cx);
 6699        editor.handle_input("{", window, cx);
 6700        editor.handle_input("{", window, cx);
 6701        assert_eq!(
 6702            editor.text(cx),
 6703            "
 6704                {{{a}}}
 6705                {{{b}}}
 6706                {{{c}}}
 6707            "
 6708            .unindent()
 6709        );
 6710        assert_eq!(
 6711            editor.selections.display_ranges(cx),
 6712            [
 6713                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6714                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6715                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6716            ]
 6717        );
 6718
 6719        editor.undo(&Undo, window, cx);
 6720        editor.undo(&Undo, window, cx);
 6721        editor.undo(&Undo, window, cx);
 6722        assert_eq!(
 6723            editor.text(cx),
 6724            "
 6725                a
 6726                b
 6727                c
 6728            "
 6729            .unindent()
 6730        );
 6731        assert_eq!(
 6732            editor.selections.display_ranges(cx),
 6733            [
 6734                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6735                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6736                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6737            ]
 6738        );
 6739
 6740        // Ensure inserting the first character of a multi-byte bracket pair
 6741        // doesn't surround the selections with the bracket.
 6742        editor.handle_input("/", window, cx);
 6743        assert_eq!(
 6744            editor.text(cx),
 6745            "
 6746                /
 6747                /
 6748                /
 6749            "
 6750            .unindent()
 6751        );
 6752        assert_eq!(
 6753            editor.selections.display_ranges(cx),
 6754            [
 6755                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6756                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6757                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6758            ]
 6759        );
 6760
 6761        editor.undo(&Undo, window, cx);
 6762        assert_eq!(
 6763            editor.text(cx),
 6764            "
 6765                a
 6766                b
 6767                c
 6768            "
 6769            .unindent()
 6770        );
 6771        assert_eq!(
 6772            editor.selections.display_ranges(cx),
 6773            [
 6774                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6775                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6776                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6777            ]
 6778        );
 6779
 6780        // Ensure inserting the last character of a multi-byte bracket pair
 6781        // doesn't surround the selections with the bracket.
 6782        editor.handle_input("*", window, cx);
 6783        assert_eq!(
 6784            editor.text(cx),
 6785            "
 6786                *
 6787                *
 6788                *
 6789            "
 6790            .unindent()
 6791        );
 6792        assert_eq!(
 6793            editor.selections.display_ranges(cx),
 6794            [
 6795                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6796                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6797                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6798            ]
 6799        );
 6800    });
 6801}
 6802
 6803#[gpui::test]
 6804async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6805    init_test(cx, |_| {});
 6806
 6807    let language = Arc::new(Language::new(
 6808        LanguageConfig {
 6809            brackets: BracketPairConfig {
 6810                pairs: vec![BracketPair {
 6811                    start: "{".to_string(),
 6812                    end: "}".to_string(),
 6813                    close: true,
 6814                    surround: true,
 6815                    newline: true,
 6816                }],
 6817                ..Default::default()
 6818            },
 6819            autoclose_before: "}".to_string(),
 6820            ..Default::default()
 6821        },
 6822        Some(tree_sitter_rust::LANGUAGE.into()),
 6823    ));
 6824
 6825    let text = r#"
 6826        a
 6827        b
 6828        c
 6829    "#
 6830    .unindent();
 6831
 6832    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6833    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6834    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6835    editor
 6836        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6837        .await;
 6838
 6839    editor.update_in(cx, |editor, window, cx| {
 6840        editor.change_selections(None, window, cx, |s| {
 6841            s.select_ranges([
 6842                Point::new(0, 1)..Point::new(0, 1),
 6843                Point::new(1, 1)..Point::new(1, 1),
 6844                Point::new(2, 1)..Point::new(2, 1),
 6845            ])
 6846        });
 6847
 6848        editor.handle_input("{", window, cx);
 6849        editor.handle_input("{", window, cx);
 6850        editor.handle_input("_", window, cx);
 6851        assert_eq!(
 6852            editor.text(cx),
 6853            "
 6854                a{{_}}
 6855                b{{_}}
 6856                c{{_}}
 6857            "
 6858            .unindent()
 6859        );
 6860        assert_eq!(
 6861            editor.selections.ranges::<Point>(cx),
 6862            [
 6863                Point::new(0, 4)..Point::new(0, 4),
 6864                Point::new(1, 4)..Point::new(1, 4),
 6865                Point::new(2, 4)..Point::new(2, 4)
 6866            ]
 6867        );
 6868
 6869        editor.backspace(&Default::default(), window, cx);
 6870        editor.backspace(&Default::default(), window, cx);
 6871        assert_eq!(
 6872            editor.text(cx),
 6873            "
 6874                a{}
 6875                b{}
 6876                c{}
 6877            "
 6878            .unindent()
 6879        );
 6880        assert_eq!(
 6881            editor.selections.ranges::<Point>(cx),
 6882            [
 6883                Point::new(0, 2)..Point::new(0, 2),
 6884                Point::new(1, 2)..Point::new(1, 2),
 6885                Point::new(2, 2)..Point::new(2, 2)
 6886            ]
 6887        );
 6888
 6889        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6890        assert_eq!(
 6891            editor.text(cx),
 6892            "
 6893                a
 6894                b
 6895                c
 6896            "
 6897            .unindent()
 6898        );
 6899        assert_eq!(
 6900            editor.selections.ranges::<Point>(cx),
 6901            [
 6902                Point::new(0, 1)..Point::new(0, 1),
 6903                Point::new(1, 1)..Point::new(1, 1),
 6904                Point::new(2, 1)..Point::new(2, 1)
 6905            ]
 6906        );
 6907    });
 6908}
 6909
 6910#[gpui::test]
 6911async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 6912    init_test(cx, |settings| {
 6913        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6914    });
 6915
 6916    let mut cx = EditorTestContext::new(cx).await;
 6917
 6918    let language = Arc::new(Language::new(
 6919        LanguageConfig {
 6920            brackets: BracketPairConfig {
 6921                pairs: vec![
 6922                    BracketPair {
 6923                        start: "{".to_string(),
 6924                        end: "}".to_string(),
 6925                        close: true,
 6926                        surround: true,
 6927                        newline: true,
 6928                    },
 6929                    BracketPair {
 6930                        start: "(".to_string(),
 6931                        end: ")".to_string(),
 6932                        close: true,
 6933                        surround: true,
 6934                        newline: true,
 6935                    },
 6936                    BracketPair {
 6937                        start: "[".to_string(),
 6938                        end: "]".to_string(),
 6939                        close: false,
 6940                        surround: true,
 6941                        newline: true,
 6942                    },
 6943                ],
 6944                ..Default::default()
 6945            },
 6946            autoclose_before: "})]".to_string(),
 6947            ..Default::default()
 6948        },
 6949        Some(tree_sitter_rust::LANGUAGE.into()),
 6950    ));
 6951
 6952    cx.language_registry().add(language.clone());
 6953    cx.update_buffer(|buffer, cx| {
 6954        buffer.set_language(Some(language), cx);
 6955    });
 6956
 6957    cx.set_state(
 6958        &"
 6959            {(ˇ)}
 6960            [[ˇ]]
 6961            {(ˇ)}
 6962        "
 6963        .unindent(),
 6964    );
 6965
 6966    cx.update_editor(|editor, window, cx| {
 6967        editor.backspace(&Default::default(), window, cx);
 6968        editor.backspace(&Default::default(), window, cx);
 6969    });
 6970
 6971    cx.assert_editor_state(
 6972        &"
 6973            ˇ
 6974            ˇ]]
 6975            ˇ
 6976        "
 6977        .unindent(),
 6978    );
 6979
 6980    cx.update_editor(|editor, window, cx| {
 6981        editor.handle_input("{", window, cx);
 6982        editor.handle_input("{", window, cx);
 6983        editor.move_right(&MoveRight, window, cx);
 6984        editor.move_right(&MoveRight, window, cx);
 6985        editor.move_left(&MoveLeft, window, cx);
 6986        editor.move_left(&MoveLeft, window, cx);
 6987        editor.backspace(&Default::default(), window, cx);
 6988    });
 6989
 6990    cx.assert_editor_state(
 6991        &"
 6992            {ˇ}
 6993            {ˇ}]]
 6994            {ˇ}
 6995        "
 6996        .unindent(),
 6997    );
 6998
 6999    cx.update_editor(|editor, window, cx| {
 7000        editor.backspace(&Default::default(), window, cx);
 7001    });
 7002
 7003    cx.assert_editor_state(
 7004        &"
 7005            ˇ
 7006            ˇ]]
 7007            ˇ
 7008        "
 7009        .unindent(),
 7010    );
 7011}
 7012
 7013#[gpui::test]
 7014async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7015    init_test(cx, |_| {});
 7016
 7017    let language = Arc::new(Language::new(
 7018        LanguageConfig::default(),
 7019        Some(tree_sitter_rust::LANGUAGE.into()),
 7020    ));
 7021
 7022    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7023    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7024    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7025    editor
 7026        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7027        .await;
 7028
 7029    editor.update_in(cx, |editor, window, cx| {
 7030        editor.set_auto_replace_emoji_shortcode(true);
 7031
 7032        editor.handle_input("Hello ", window, cx);
 7033        editor.handle_input(":wave", window, cx);
 7034        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7035
 7036        editor.handle_input(":", window, cx);
 7037        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7038
 7039        editor.handle_input(" :smile", window, cx);
 7040        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7041
 7042        editor.handle_input(":", window, cx);
 7043        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7044
 7045        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7046        editor.handle_input(":wave", window, cx);
 7047        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7048
 7049        editor.handle_input(":", window, cx);
 7050        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7051
 7052        editor.handle_input(":1", window, cx);
 7053        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7054
 7055        editor.handle_input(":", window, cx);
 7056        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7057
 7058        // Ensure shortcode does not get replaced when it is part of a word
 7059        editor.handle_input(" Test:wave", window, cx);
 7060        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7061
 7062        editor.handle_input(":", window, cx);
 7063        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7064
 7065        editor.set_auto_replace_emoji_shortcode(false);
 7066
 7067        // Ensure shortcode does not get replaced when auto replace is off
 7068        editor.handle_input(" :wave", window, cx);
 7069        assert_eq!(
 7070            editor.text(cx),
 7071            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7072        );
 7073
 7074        editor.handle_input(":", window, cx);
 7075        assert_eq!(
 7076            editor.text(cx),
 7077            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7078        );
 7079    });
 7080}
 7081
 7082#[gpui::test]
 7083async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7084    init_test(cx, |_| {});
 7085
 7086    let (text, insertion_ranges) = marked_text_ranges(
 7087        indoc! {"
 7088            ˇ
 7089        "},
 7090        false,
 7091    );
 7092
 7093    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7094    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7095
 7096    _ = editor.update_in(cx, |editor, window, cx| {
 7097        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7098
 7099        editor
 7100            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7101            .unwrap();
 7102
 7103        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7104            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7105            assert_eq!(editor.text(cx), expected_text);
 7106            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7107        }
 7108
 7109        assert(
 7110            editor,
 7111            cx,
 7112            indoc! {"
 7113            type «» =•
 7114            "},
 7115        );
 7116
 7117        assert!(editor.context_menu_visible(), "There should be a matches");
 7118    });
 7119}
 7120
 7121#[gpui::test]
 7122async fn test_snippets(cx: &mut TestAppContext) {
 7123    init_test(cx, |_| {});
 7124
 7125    let (text, insertion_ranges) = marked_text_ranges(
 7126        indoc! {"
 7127            a.ˇ b
 7128            a.ˇ b
 7129            a.ˇ b
 7130        "},
 7131        false,
 7132    );
 7133
 7134    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7135    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7136
 7137    editor.update_in(cx, |editor, window, cx| {
 7138        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7139
 7140        editor
 7141            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7142            .unwrap();
 7143
 7144        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7145            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7146            assert_eq!(editor.text(cx), expected_text);
 7147            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7148        }
 7149
 7150        assert(
 7151            editor,
 7152            cx,
 7153            indoc! {"
 7154                a.f(«one», two, «three») b
 7155                a.f(«one», two, «three») b
 7156                a.f(«one», two, «three») b
 7157            "},
 7158        );
 7159
 7160        // Can't move earlier than the first tab stop
 7161        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7162        assert(
 7163            editor,
 7164            cx,
 7165            indoc! {"
 7166                a.f(«one», two, «three») b
 7167                a.f(«one», two, «three») b
 7168                a.f(«one», two, «three») b
 7169            "},
 7170        );
 7171
 7172        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7173        assert(
 7174            editor,
 7175            cx,
 7176            indoc! {"
 7177                a.f(one, «two», three) b
 7178                a.f(one, «two», three) b
 7179                a.f(one, «two», three) b
 7180            "},
 7181        );
 7182
 7183        editor.move_to_prev_snippet_tabstop(window, cx);
 7184        assert(
 7185            editor,
 7186            cx,
 7187            indoc! {"
 7188                a.f(«one», two, «three») b
 7189                a.f(«one», two, «three») b
 7190                a.f(«one», two, «three») b
 7191            "},
 7192        );
 7193
 7194        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7195        assert(
 7196            editor,
 7197            cx,
 7198            indoc! {"
 7199                a.f(one, «two», three) b
 7200                a.f(one, «two», three) b
 7201                a.f(one, «two», three) b
 7202            "},
 7203        );
 7204        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7205        assert(
 7206            editor,
 7207            cx,
 7208            indoc! {"
 7209                a.f(one, two, three)ˇ b
 7210                a.f(one, two, three)ˇ b
 7211                a.f(one, two, three)ˇ b
 7212            "},
 7213        );
 7214
 7215        // As soon as the last tab stop is reached, snippet state is gone
 7216        editor.move_to_prev_snippet_tabstop(window, cx);
 7217        assert(
 7218            editor,
 7219            cx,
 7220            indoc! {"
 7221                a.f(one, two, three)ˇ b
 7222                a.f(one, two, three)ˇ b
 7223                a.f(one, two, three)ˇ b
 7224            "},
 7225        );
 7226    });
 7227}
 7228
 7229#[gpui::test]
 7230async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7231    init_test(cx, |_| {});
 7232
 7233    let fs = FakeFs::new(cx.executor());
 7234    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7235
 7236    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7237
 7238    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7239    language_registry.add(rust_lang());
 7240    let mut fake_servers = language_registry.register_fake_lsp(
 7241        "Rust",
 7242        FakeLspAdapter {
 7243            capabilities: lsp::ServerCapabilities {
 7244                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7245                ..Default::default()
 7246            },
 7247            ..Default::default()
 7248        },
 7249    );
 7250
 7251    let buffer = project
 7252        .update(cx, |project, cx| {
 7253            project.open_local_buffer(path!("/file.rs"), cx)
 7254        })
 7255        .await
 7256        .unwrap();
 7257
 7258    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7259    let (editor, cx) = cx.add_window_view(|window, cx| {
 7260        build_editor_with_project(project.clone(), buffer, window, cx)
 7261    });
 7262    editor.update_in(cx, |editor, window, cx| {
 7263        editor.set_text("one\ntwo\nthree\n", window, cx)
 7264    });
 7265    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7266
 7267    cx.executor().start_waiting();
 7268    let fake_server = fake_servers.next().await.unwrap();
 7269
 7270    let save = editor
 7271        .update_in(cx, |editor, window, cx| {
 7272            editor.save(true, project.clone(), window, cx)
 7273        })
 7274        .unwrap();
 7275    fake_server
 7276        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7277            assert_eq!(
 7278                params.text_document.uri,
 7279                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7280            );
 7281            assert_eq!(params.options.tab_size, 4);
 7282            Ok(Some(vec![lsp::TextEdit::new(
 7283                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7284                ", ".to_string(),
 7285            )]))
 7286        })
 7287        .next()
 7288        .await;
 7289    cx.executor().start_waiting();
 7290    save.await;
 7291
 7292    assert_eq!(
 7293        editor.update(cx, |editor, cx| editor.text(cx)),
 7294        "one, two\nthree\n"
 7295    );
 7296    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7297
 7298    editor.update_in(cx, |editor, window, cx| {
 7299        editor.set_text("one\ntwo\nthree\n", window, cx)
 7300    });
 7301    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7302
 7303    // Ensure we can still save even if formatting hangs.
 7304    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7305        assert_eq!(
 7306            params.text_document.uri,
 7307            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7308        );
 7309        futures::future::pending::<()>().await;
 7310        unreachable!()
 7311    });
 7312    let save = editor
 7313        .update_in(cx, |editor, window, cx| {
 7314            editor.save(true, project.clone(), window, cx)
 7315        })
 7316        .unwrap();
 7317    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7318    cx.executor().start_waiting();
 7319    save.await;
 7320    assert_eq!(
 7321        editor.update(cx, |editor, cx| editor.text(cx)),
 7322        "one\ntwo\nthree\n"
 7323    );
 7324    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7325
 7326    // For non-dirty buffer, no formatting request should be sent
 7327    let save = editor
 7328        .update_in(cx, |editor, window, cx| {
 7329            editor.save(true, project.clone(), window, cx)
 7330        })
 7331        .unwrap();
 7332    let _pending_format_request = fake_server
 7333        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7334            panic!("Should not be invoked on non-dirty buffer");
 7335        })
 7336        .next();
 7337    cx.executor().start_waiting();
 7338    save.await;
 7339
 7340    // Set rust language override and assert overridden tabsize is sent to language server
 7341    update_test_language_settings(cx, |settings| {
 7342        settings.languages.insert(
 7343            "Rust".into(),
 7344            LanguageSettingsContent {
 7345                tab_size: NonZeroU32::new(8),
 7346                ..Default::default()
 7347            },
 7348        );
 7349    });
 7350
 7351    editor.update_in(cx, |editor, window, cx| {
 7352        editor.set_text("somehting_new\n", window, cx)
 7353    });
 7354    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7355    let save = editor
 7356        .update_in(cx, |editor, window, cx| {
 7357            editor.save(true, project.clone(), window, cx)
 7358        })
 7359        .unwrap();
 7360    fake_server
 7361        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7362            assert_eq!(
 7363                params.text_document.uri,
 7364                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7365            );
 7366            assert_eq!(params.options.tab_size, 8);
 7367            Ok(Some(vec![]))
 7368        })
 7369        .next()
 7370        .await;
 7371    cx.executor().start_waiting();
 7372    save.await;
 7373}
 7374
 7375#[gpui::test]
 7376async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7377    init_test(cx, |_| {});
 7378
 7379    let cols = 4;
 7380    let rows = 10;
 7381    let sample_text_1 = sample_text(rows, cols, 'a');
 7382    assert_eq!(
 7383        sample_text_1,
 7384        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7385    );
 7386    let sample_text_2 = sample_text(rows, cols, 'l');
 7387    assert_eq!(
 7388        sample_text_2,
 7389        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7390    );
 7391    let sample_text_3 = sample_text(rows, cols, 'v');
 7392    assert_eq!(
 7393        sample_text_3,
 7394        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7395    );
 7396
 7397    let fs = FakeFs::new(cx.executor());
 7398    fs.insert_tree(
 7399        path!("/a"),
 7400        json!({
 7401            "main.rs": sample_text_1,
 7402            "other.rs": sample_text_2,
 7403            "lib.rs": sample_text_3,
 7404        }),
 7405    )
 7406    .await;
 7407
 7408    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7409    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7410    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7411
 7412    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7413    language_registry.add(rust_lang());
 7414    let mut fake_servers = language_registry.register_fake_lsp(
 7415        "Rust",
 7416        FakeLspAdapter {
 7417            capabilities: lsp::ServerCapabilities {
 7418                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7419                ..Default::default()
 7420            },
 7421            ..Default::default()
 7422        },
 7423    );
 7424
 7425    let worktree = project.update(cx, |project, cx| {
 7426        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7427        assert_eq!(worktrees.len(), 1);
 7428        worktrees.pop().unwrap()
 7429    });
 7430    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7431
 7432    let buffer_1 = project
 7433        .update(cx, |project, cx| {
 7434            project.open_buffer((worktree_id, "main.rs"), cx)
 7435        })
 7436        .await
 7437        .unwrap();
 7438    let buffer_2 = project
 7439        .update(cx, |project, cx| {
 7440            project.open_buffer((worktree_id, "other.rs"), cx)
 7441        })
 7442        .await
 7443        .unwrap();
 7444    let buffer_3 = project
 7445        .update(cx, |project, cx| {
 7446            project.open_buffer((worktree_id, "lib.rs"), cx)
 7447        })
 7448        .await
 7449        .unwrap();
 7450
 7451    let multi_buffer = cx.new(|cx| {
 7452        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7453        multi_buffer.push_excerpts(
 7454            buffer_1.clone(),
 7455            [
 7456                ExcerptRange {
 7457                    context: Point::new(0, 0)..Point::new(3, 0),
 7458                    primary: None,
 7459                },
 7460                ExcerptRange {
 7461                    context: Point::new(5, 0)..Point::new(7, 0),
 7462                    primary: None,
 7463                },
 7464                ExcerptRange {
 7465                    context: Point::new(9, 0)..Point::new(10, 4),
 7466                    primary: None,
 7467                },
 7468            ],
 7469            cx,
 7470        );
 7471        multi_buffer.push_excerpts(
 7472            buffer_2.clone(),
 7473            [
 7474                ExcerptRange {
 7475                    context: Point::new(0, 0)..Point::new(3, 0),
 7476                    primary: None,
 7477                },
 7478                ExcerptRange {
 7479                    context: Point::new(5, 0)..Point::new(7, 0),
 7480                    primary: None,
 7481                },
 7482                ExcerptRange {
 7483                    context: Point::new(9, 0)..Point::new(10, 4),
 7484                    primary: None,
 7485                },
 7486            ],
 7487            cx,
 7488        );
 7489        multi_buffer.push_excerpts(
 7490            buffer_3.clone(),
 7491            [
 7492                ExcerptRange {
 7493                    context: Point::new(0, 0)..Point::new(3, 0),
 7494                    primary: None,
 7495                },
 7496                ExcerptRange {
 7497                    context: Point::new(5, 0)..Point::new(7, 0),
 7498                    primary: None,
 7499                },
 7500                ExcerptRange {
 7501                    context: Point::new(9, 0)..Point::new(10, 4),
 7502                    primary: None,
 7503                },
 7504            ],
 7505            cx,
 7506        );
 7507        multi_buffer
 7508    });
 7509    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7510        Editor::new(
 7511            EditorMode::Full,
 7512            multi_buffer,
 7513            Some(project.clone()),
 7514            true,
 7515            window,
 7516            cx,
 7517        )
 7518    });
 7519
 7520    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7521        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7522            s.select_ranges(Some(1..2))
 7523        });
 7524        editor.insert("|one|two|three|", window, cx);
 7525    });
 7526    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7527    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7528        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7529            s.select_ranges(Some(60..70))
 7530        });
 7531        editor.insert("|four|five|six|", window, cx);
 7532    });
 7533    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7534
 7535    // First two buffers should be edited, but not the third one.
 7536    assert_eq!(
 7537        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7538        "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}",
 7539    );
 7540    buffer_1.update(cx, |buffer, _| {
 7541        assert!(buffer.is_dirty());
 7542        assert_eq!(
 7543            buffer.text(),
 7544            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7545        )
 7546    });
 7547    buffer_2.update(cx, |buffer, _| {
 7548        assert!(buffer.is_dirty());
 7549        assert_eq!(
 7550            buffer.text(),
 7551            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7552        )
 7553    });
 7554    buffer_3.update(cx, |buffer, _| {
 7555        assert!(!buffer.is_dirty());
 7556        assert_eq!(buffer.text(), sample_text_3,)
 7557    });
 7558    cx.executor().run_until_parked();
 7559
 7560    cx.executor().start_waiting();
 7561    let save = multi_buffer_editor
 7562        .update_in(cx, |editor, window, cx| {
 7563            editor.save(true, project.clone(), window, cx)
 7564        })
 7565        .unwrap();
 7566
 7567    let fake_server = fake_servers.next().await.unwrap();
 7568    fake_server
 7569        .server
 7570        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7571            Ok(Some(vec![lsp::TextEdit::new(
 7572                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7573                format!("[{} formatted]", params.text_document.uri),
 7574            )]))
 7575        })
 7576        .detach();
 7577    save.await;
 7578
 7579    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7580    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7581    assert_eq!(
 7582        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7583        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}"),
 7584    );
 7585    buffer_1.update(cx, |buffer, _| {
 7586        assert!(!buffer.is_dirty());
 7587        assert_eq!(
 7588            buffer.text(),
 7589            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7590        )
 7591    });
 7592    buffer_2.update(cx, |buffer, _| {
 7593        assert!(!buffer.is_dirty());
 7594        assert_eq!(
 7595            buffer.text(),
 7596            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7597        )
 7598    });
 7599    buffer_3.update(cx, |buffer, _| {
 7600        assert!(!buffer.is_dirty());
 7601        assert_eq!(buffer.text(), sample_text_3,)
 7602    });
 7603}
 7604
 7605#[gpui::test]
 7606async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7607    init_test(cx, |_| {});
 7608
 7609    let fs = FakeFs::new(cx.executor());
 7610    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7611
 7612    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7613
 7614    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7615    language_registry.add(rust_lang());
 7616    let mut fake_servers = language_registry.register_fake_lsp(
 7617        "Rust",
 7618        FakeLspAdapter {
 7619            capabilities: lsp::ServerCapabilities {
 7620                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7621                ..Default::default()
 7622            },
 7623            ..Default::default()
 7624        },
 7625    );
 7626
 7627    let buffer = project
 7628        .update(cx, |project, cx| {
 7629            project.open_local_buffer(path!("/file.rs"), cx)
 7630        })
 7631        .await
 7632        .unwrap();
 7633
 7634    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7635    let (editor, cx) = cx.add_window_view(|window, cx| {
 7636        build_editor_with_project(project.clone(), buffer, window, cx)
 7637    });
 7638    editor.update_in(cx, |editor, window, cx| {
 7639        editor.set_text("one\ntwo\nthree\n", window, cx)
 7640    });
 7641    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7642
 7643    cx.executor().start_waiting();
 7644    let fake_server = fake_servers.next().await.unwrap();
 7645
 7646    let save = editor
 7647        .update_in(cx, |editor, window, cx| {
 7648            editor.save(true, project.clone(), window, cx)
 7649        })
 7650        .unwrap();
 7651    fake_server
 7652        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7653            assert_eq!(
 7654                params.text_document.uri,
 7655                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7656            );
 7657            assert_eq!(params.options.tab_size, 4);
 7658            Ok(Some(vec![lsp::TextEdit::new(
 7659                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7660                ", ".to_string(),
 7661            )]))
 7662        })
 7663        .next()
 7664        .await;
 7665    cx.executor().start_waiting();
 7666    save.await;
 7667    assert_eq!(
 7668        editor.update(cx, |editor, cx| editor.text(cx)),
 7669        "one, two\nthree\n"
 7670    );
 7671    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7672
 7673    editor.update_in(cx, |editor, window, cx| {
 7674        editor.set_text("one\ntwo\nthree\n", window, cx)
 7675    });
 7676    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7677
 7678    // Ensure we can still save even if formatting hangs.
 7679    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7680        move |params, _| async move {
 7681            assert_eq!(
 7682                params.text_document.uri,
 7683                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7684            );
 7685            futures::future::pending::<()>().await;
 7686            unreachable!()
 7687        },
 7688    );
 7689    let save = editor
 7690        .update_in(cx, |editor, window, cx| {
 7691            editor.save(true, project.clone(), window, cx)
 7692        })
 7693        .unwrap();
 7694    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7695    cx.executor().start_waiting();
 7696    save.await;
 7697    assert_eq!(
 7698        editor.update(cx, |editor, cx| editor.text(cx)),
 7699        "one\ntwo\nthree\n"
 7700    );
 7701    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7702
 7703    // For non-dirty buffer, no formatting request should be sent
 7704    let save = editor
 7705        .update_in(cx, |editor, window, cx| {
 7706            editor.save(true, project.clone(), window, cx)
 7707        })
 7708        .unwrap();
 7709    let _pending_format_request = fake_server
 7710        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7711            panic!("Should not be invoked on non-dirty buffer");
 7712        })
 7713        .next();
 7714    cx.executor().start_waiting();
 7715    save.await;
 7716
 7717    // Set Rust language override and assert overridden tabsize is sent to language server
 7718    update_test_language_settings(cx, |settings| {
 7719        settings.languages.insert(
 7720            "Rust".into(),
 7721            LanguageSettingsContent {
 7722                tab_size: NonZeroU32::new(8),
 7723                ..Default::default()
 7724            },
 7725        );
 7726    });
 7727
 7728    editor.update_in(cx, |editor, window, cx| {
 7729        editor.set_text("somehting_new\n", window, cx)
 7730    });
 7731    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7732    let save = editor
 7733        .update_in(cx, |editor, window, cx| {
 7734            editor.save(true, project.clone(), window, cx)
 7735        })
 7736        .unwrap();
 7737    fake_server
 7738        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7739            assert_eq!(
 7740                params.text_document.uri,
 7741                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7742            );
 7743            assert_eq!(params.options.tab_size, 8);
 7744            Ok(Some(vec![]))
 7745        })
 7746        .next()
 7747        .await;
 7748    cx.executor().start_waiting();
 7749    save.await;
 7750}
 7751
 7752#[gpui::test]
 7753async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7754    init_test(cx, |settings| {
 7755        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7756            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7757        ))
 7758    });
 7759
 7760    let fs = FakeFs::new(cx.executor());
 7761    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7762
 7763    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7764
 7765    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7766    language_registry.add(Arc::new(Language::new(
 7767        LanguageConfig {
 7768            name: "Rust".into(),
 7769            matcher: LanguageMatcher {
 7770                path_suffixes: vec!["rs".to_string()],
 7771                ..Default::default()
 7772            },
 7773            ..LanguageConfig::default()
 7774        },
 7775        Some(tree_sitter_rust::LANGUAGE.into()),
 7776    )));
 7777    update_test_language_settings(cx, |settings| {
 7778        // Enable Prettier formatting for the same buffer, and ensure
 7779        // LSP is called instead of Prettier.
 7780        settings.defaults.prettier = Some(PrettierSettings {
 7781            allowed: true,
 7782            ..PrettierSettings::default()
 7783        });
 7784    });
 7785    let mut fake_servers = language_registry.register_fake_lsp(
 7786        "Rust",
 7787        FakeLspAdapter {
 7788            capabilities: lsp::ServerCapabilities {
 7789                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7790                ..Default::default()
 7791            },
 7792            ..Default::default()
 7793        },
 7794    );
 7795
 7796    let buffer = project
 7797        .update(cx, |project, cx| {
 7798            project.open_local_buffer(path!("/file.rs"), cx)
 7799        })
 7800        .await
 7801        .unwrap();
 7802
 7803    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7804    let (editor, cx) = cx.add_window_view(|window, cx| {
 7805        build_editor_with_project(project.clone(), buffer, window, cx)
 7806    });
 7807    editor.update_in(cx, |editor, window, cx| {
 7808        editor.set_text("one\ntwo\nthree\n", window, cx)
 7809    });
 7810
 7811    cx.executor().start_waiting();
 7812    let fake_server = fake_servers.next().await.unwrap();
 7813
 7814    let format = editor
 7815        .update_in(cx, |editor, window, cx| {
 7816            editor.perform_format(
 7817                project.clone(),
 7818                FormatTrigger::Manual,
 7819                FormatTarget::Buffers,
 7820                window,
 7821                cx,
 7822            )
 7823        })
 7824        .unwrap();
 7825    fake_server
 7826        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7827            assert_eq!(
 7828                params.text_document.uri,
 7829                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7830            );
 7831            assert_eq!(params.options.tab_size, 4);
 7832            Ok(Some(vec![lsp::TextEdit::new(
 7833                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7834                ", ".to_string(),
 7835            )]))
 7836        })
 7837        .next()
 7838        .await;
 7839    cx.executor().start_waiting();
 7840    format.await;
 7841    assert_eq!(
 7842        editor.update(cx, |editor, cx| editor.text(cx)),
 7843        "one, two\nthree\n"
 7844    );
 7845
 7846    editor.update_in(cx, |editor, window, cx| {
 7847        editor.set_text("one\ntwo\nthree\n", window, cx)
 7848    });
 7849    // Ensure we don't lock if formatting hangs.
 7850    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7851        assert_eq!(
 7852            params.text_document.uri,
 7853            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7854        );
 7855        futures::future::pending::<()>().await;
 7856        unreachable!()
 7857    });
 7858    let format = editor
 7859        .update_in(cx, |editor, window, cx| {
 7860            editor.perform_format(
 7861                project,
 7862                FormatTrigger::Manual,
 7863                FormatTarget::Buffers,
 7864                window,
 7865                cx,
 7866            )
 7867        })
 7868        .unwrap();
 7869    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7870    cx.executor().start_waiting();
 7871    format.await;
 7872    assert_eq!(
 7873        editor.update(cx, |editor, cx| editor.text(cx)),
 7874        "one\ntwo\nthree\n"
 7875    );
 7876}
 7877
 7878#[gpui::test]
 7879async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 7880    init_test(cx, |settings| {
 7881        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7882            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7883        ))
 7884    });
 7885
 7886    let fs = FakeFs::new(cx.executor());
 7887    fs.insert_file(path!("/file.ts"), Default::default()).await;
 7888
 7889    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7890
 7891    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7892    language_registry.add(Arc::new(Language::new(
 7893        LanguageConfig {
 7894            name: "TypeScript".into(),
 7895            matcher: LanguageMatcher {
 7896                path_suffixes: vec!["ts".to_string()],
 7897                ..Default::default()
 7898            },
 7899            ..LanguageConfig::default()
 7900        },
 7901        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 7902    )));
 7903    update_test_language_settings(cx, |settings| {
 7904        settings.defaults.prettier = Some(PrettierSettings {
 7905            allowed: true,
 7906            ..PrettierSettings::default()
 7907        });
 7908    });
 7909    let mut fake_servers = language_registry.register_fake_lsp(
 7910        "TypeScript",
 7911        FakeLspAdapter {
 7912            capabilities: lsp::ServerCapabilities {
 7913                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 7914                ..Default::default()
 7915            },
 7916            ..Default::default()
 7917        },
 7918    );
 7919
 7920    let buffer = project
 7921        .update(cx, |project, cx| {
 7922            project.open_local_buffer(path!("/file.ts"), cx)
 7923        })
 7924        .await
 7925        .unwrap();
 7926
 7927    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7928    let (editor, cx) = cx.add_window_view(|window, cx| {
 7929        build_editor_with_project(project.clone(), buffer, window, cx)
 7930    });
 7931    editor.update_in(cx, |editor, window, cx| {
 7932        editor.set_text(
 7933            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 7934            window,
 7935            cx,
 7936        )
 7937    });
 7938
 7939    cx.executor().start_waiting();
 7940    let fake_server = fake_servers.next().await.unwrap();
 7941
 7942    let format = editor
 7943        .update_in(cx, |editor, window, cx| {
 7944            editor.perform_code_action_kind(
 7945                project.clone(),
 7946                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 7947                window,
 7948                cx,
 7949            )
 7950        })
 7951        .unwrap();
 7952    fake_server
 7953        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 7954            assert_eq!(
 7955                params.text_document.uri,
 7956                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 7957            );
 7958            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 7959                lsp::CodeAction {
 7960                    title: "Organize Imports".to_string(),
 7961                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 7962                    edit: Some(lsp::WorkspaceEdit {
 7963                        changes: Some(
 7964                            [(
 7965                                params.text_document.uri.clone(),
 7966                                vec![lsp::TextEdit::new(
 7967                                    lsp::Range::new(
 7968                                        lsp::Position::new(1, 0),
 7969                                        lsp::Position::new(2, 0),
 7970                                    ),
 7971                                    "".to_string(),
 7972                                )],
 7973                            )]
 7974                            .into_iter()
 7975                            .collect(),
 7976                        ),
 7977                        ..Default::default()
 7978                    }),
 7979                    ..Default::default()
 7980                },
 7981            )]))
 7982        })
 7983        .next()
 7984        .await;
 7985    cx.executor().start_waiting();
 7986    format.await;
 7987    assert_eq!(
 7988        editor.update(cx, |editor, cx| editor.text(cx)),
 7989        "import { a } from 'module';\n\nconst x = a;\n"
 7990    );
 7991
 7992    editor.update_in(cx, |editor, window, cx| {
 7993        editor.set_text(
 7994            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 7995            window,
 7996            cx,
 7997        )
 7998    });
 7999    // Ensure we don't lock if code action hangs.
 8000    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8001        move |params, _| async move {
 8002            assert_eq!(
 8003                params.text_document.uri,
 8004                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8005            );
 8006            futures::future::pending::<()>().await;
 8007            unreachable!()
 8008        },
 8009    );
 8010    let format = editor
 8011        .update_in(cx, |editor, window, cx| {
 8012            editor.perform_code_action_kind(
 8013                project,
 8014                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8015                window,
 8016                cx,
 8017            )
 8018        })
 8019        .unwrap();
 8020    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8021    cx.executor().start_waiting();
 8022    format.await;
 8023    assert_eq!(
 8024        editor.update(cx, |editor, cx| editor.text(cx)),
 8025        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8026    );
 8027}
 8028
 8029#[gpui::test]
 8030async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8031    init_test(cx, |_| {});
 8032
 8033    let mut cx = EditorLspTestContext::new_rust(
 8034        lsp::ServerCapabilities {
 8035            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8036            ..Default::default()
 8037        },
 8038        cx,
 8039    )
 8040    .await;
 8041
 8042    cx.set_state(indoc! {"
 8043        one.twoˇ
 8044    "});
 8045
 8046    // The format request takes a long time. When it completes, it inserts
 8047    // a newline and an indent before the `.`
 8048    cx.lsp
 8049        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8050            let executor = cx.background_executor().clone();
 8051            async move {
 8052                executor.timer(Duration::from_millis(100)).await;
 8053                Ok(Some(vec![lsp::TextEdit {
 8054                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8055                    new_text: "\n    ".into(),
 8056                }]))
 8057            }
 8058        });
 8059
 8060    // Submit a format request.
 8061    let format_1 = cx
 8062        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8063        .unwrap();
 8064    cx.executor().run_until_parked();
 8065
 8066    // Submit a second format request.
 8067    let format_2 = cx
 8068        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8069        .unwrap();
 8070    cx.executor().run_until_parked();
 8071
 8072    // Wait for both format requests to complete
 8073    cx.executor().advance_clock(Duration::from_millis(200));
 8074    cx.executor().start_waiting();
 8075    format_1.await.unwrap();
 8076    cx.executor().start_waiting();
 8077    format_2.await.unwrap();
 8078
 8079    // The formatting edits only happens once.
 8080    cx.assert_editor_state(indoc! {"
 8081        one
 8082            .twoˇ
 8083    "});
 8084}
 8085
 8086#[gpui::test]
 8087async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8088    init_test(cx, |settings| {
 8089        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8090    });
 8091
 8092    let mut cx = EditorLspTestContext::new_rust(
 8093        lsp::ServerCapabilities {
 8094            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8095            ..Default::default()
 8096        },
 8097        cx,
 8098    )
 8099    .await;
 8100
 8101    // Set up a buffer white some trailing whitespace and no trailing newline.
 8102    cx.set_state(
 8103        &[
 8104            "one ",   //
 8105            "twoˇ",   //
 8106            "three ", //
 8107            "four",   //
 8108        ]
 8109        .join("\n"),
 8110    );
 8111
 8112    // Submit a format request.
 8113    let format = cx
 8114        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8115        .unwrap();
 8116
 8117    // Record which buffer changes have been sent to the language server
 8118    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8119    cx.lsp
 8120        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8121            let buffer_changes = buffer_changes.clone();
 8122            move |params, _| {
 8123                buffer_changes.lock().extend(
 8124                    params
 8125                        .content_changes
 8126                        .into_iter()
 8127                        .map(|e| (e.range.unwrap(), e.text)),
 8128                );
 8129            }
 8130        });
 8131
 8132    // Handle formatting requests to the language server.
 8133    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8134        let buffer_changes = buffer_changes.clone();
 8135        move |_, _| {
 8136            // When formatting is requested, trailing whitespace has already been stripped,
 8137            // and the trailing newline has already been added.
 8138            assert_eq!(
 8139                &buffer_changes.lock()[1..],
 8140                &[
 8141                    (
 8142                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8143                        "".into()
 8144                    ),
 8145                    (
 8146                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8147                        "".into()
 8148                    ),
 8149                    (
 8150                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8151                        "\n".into()
 8152                    ),
 8153                ]
 8154            );
 8155
 8156            // Insert blank lines between each line of the buffer.
 8157            async move {
 8158                Ok(Some(vec![
 8159                    lsp::TextEdit {
 8160                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8161                        new_text: "\n".into(),
 8162                    },
 8163                    lsp::TextEdit {
 8164                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8165                        new_text: "\n".into(),
 8166                    },
 8167                ]))
 8168            }
 8169        }
 8170    });
 8171
 8172    // After formatting the buffer, the trailing whitespace is stripped,
 8173    // a newline is appended, and the edits provided by the language server
 8174    // have been applied.
 8175    format.await.unwrap();
 8176    cx.assert_editor_state(
 8177        &[
 8178            "one",   //
 8179            "",      //
 8180            "twoˇ",  //
 8181            "",      //
 8182            "three", //
 8183            "four",  //
 8184            "",      //
 8185        ]
 8186        .join("\n"),
 8187    );
 8188
 8189    // Undoing the formatting undoes the trailing whitespace removal, the
 8190    // trailing newline, and the LSP edits.
 8191    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8192    cx.assert_editor_state(
 8193        &[
 8194            "one ",   //
 8195            "twoˇ",   //
 8196            "three ", //
 8197            "four",   //
 8198        ]
 8199        .join("\n"),
 8200    );
 8201}
 8202
 8203#[gpui::test]
 8204async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8205    cx: &mut TestAppContext,
 8206) {
 8207    init_test(cx, |_| {});
 8208
 8209    cx.update(|cx| {
 8210        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8211            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8212                settings.auto_signature_help = Some(true);
 8213            });
 8214        });
 8215    });
 8216
 8217    let mut cx = EditorLspTestContext::new_rust(
 8218        lsp::ServerCapabilities {
 8219            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8220                ..Default::default()
 8221            }),
 8222            ..Default::default()
 8223        },
 8224        cx,
 8225    )
 8226    .await;
 8227
 8228    let language = Language::new(
 8229        LanguageConfig {
 8230            name: "Rust".into(),
 8231            brackets: BracketPairConfig {
 8232                pairs: vec![
 8233                    BracketPair {
 8234                        start: "{".to_string(),
 8235                        end: "}".to_string(),
 8236                        close: true,
 8237                        surround: true,
 8238                        newline: true,
 8239                    },
 8240                    BracketPair {
 8241                        start: "(".to_string(),
 8242                        end: ")".to_string(),
 8243                        close: true,
 8244                        surround: true,
 8245                        newline: true,
 8246                    },
 8247                    BracketPair {
 8248                        start: "/*".to_string(),
 8249                        end: " */".to_string(),
 8250                        close: true,
 8251                        surround: true,
 8252                        newline: true,
 8253                    },
 8254                    BracketPair {
 8255                        start: "[".to_string(),
 8256                        end: "]".to_string(),
 8257                        close: false,
 8258                        surround: false,
 8259                        newline: true,
 8260                    },
 8261                    BracketPair {
 8262                        start: "\"".to_string(),
 8263                        end: "\"".to_string(),
 8264                        close: true,
 8265                        surround: true,
 8266                        newline: false,
 8267                    },
 8268                    BracketPair {
 8269                        start: "<".to_string(),
 8270                        end: ">".to_string(),
 8271                        close: false,
 8272                        surround: true,
 8273                        newline: true,
 8274                    },
 8275                ],
 8276                ..Default::default()
 8277            },
 8278            autoclose_before: "})]".to_string(),
 8279            ..Default::default()
 8280        },
 8281        Some(tree_sitter_rust::LANGUAGE.into()),
 8282    );
 8283    let language = Arc::new(language);
 8284
 8285    cx.language_registry().add(language.clone());
 8286    cx.update_buffer(|buffer, cx| {
 8287        buffer.set_language(Some(language), cx);
 8288    });
 8289
 8290    cx.set_state(
 8291        &r#"
 8292            fn main() {
 8293                sampleˇ
 8294            }
 8295        "#
 8296        .unindent(),
 8297    );
 8298
 8299    cx.update_editor(|editor, window, cx| {
 8300        editor.handle_input("(", window, cx);
 8301    });
 8302    cx.assert_editor_state(
 8303        &"
 8304            fn main() {
 8305                sample(ˇ)
 8306            }
 8307        "
 8308        .unindent(),
 8309    );
 8310
 8311    let mocked_response = lsp::SignatureHelp {
 8312        signatures: vec![lsp::SignatureInformation {
 8313            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8314            documentation: None,
 8315            parameters: Some(vec![
 8316                lsp::ParameterInformation {
 8317                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8318                    documentation: None,
 8319                },
 8320                lsp::ParameterInformation {
 8321                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8322                    documentation: None,
 8323                },
 8324            ]),
 8325            active_parameter: None,
 8326        }],
 8327        active_signature: Some(0),
 8328        active_parameter: Some(0),
 8329    };
 8330    handle_signature_help_request(&mut cx, mocked_response).await;
 8331
 8332    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8333        .await;
 8334
 8335    cx.editor(|editor, _, _| {
 8336        let signature_help_state = editor.signature_help_state.popover().cloned();
 8337        assert_eq!(
 8338            signature_help_state.unwrap().label,
 8339            "param1: u8, param2: u8"
 8340        );
 8341    });
 8342}
 8343
 8344#[gpui::test]
 8345async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8346    init_test(cx, |_| {});
 8347
 8348    cx.update(|cx| {
 8349        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8350            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8351                settings.auto_signature_help = Some(false);
 8352                settings.show_signature_help_after_edits = Some(false);
 8353            });
 8354        });
 8355    });
 8356
 8357    let mut cx = EditorLspTestContext::new_rust(
 8358        lsp::ServerCapabilities {
 8359            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8360                ..Default::default()
 8361            }),
 8362            ..Default::default()
 8363        },
 8364        cx,
 8365    )
 8366    .await;
 8367
 8368    let language = Language::new(
 8369        LanguageConfig {
 8370            name: "Rust".into(),
 8371            brackets: BracketPairConfig {
 8372                pairs: vec![
 8373                    BracketPair {
 8374                        start: "{".to_string(),
 8375                        end: "}".to_string(),
 8376                        close: true,
 8377                        surround: true,
 8378                        newline: true,
 8379                    },
 8380                    BracketPair {
 8381                        start: "(".to_string(),
 8382                        end: ")".to_string(),
 8383                        close: true,
 8384                        surround: true,
 8385                        newline: true,
 8386                    },
 8387                    BracketPair {
 8388                        start: "/*".to_string(),
 8389                        end: " */".to_string(),
 8390                        close: true,
 8391                        surround: true,
 8392                        newline: true,
 8393                    },
 8394                    BracketPair {
 8395                        start: "[".to_string(),
 8396                        end: "]".to_string(),
 8397                        close: false,
 8398                        surround: false,
 8399                        newline: true,
 8400                    },
 8401                    BracketPair {
 8402                        start: "\"".to_string(),
 8403                        end: "\"".to_string(),
 8404                        close: true,
 8405                        surround: true,
 8406                        newline: false,
 8407                    },
 8408                    BracketPair {
 8409                        start: "<".to_string(),
 8410                        end: ">".to_string(),
 8411                        close: false,
 8412                        surround: true,
 8413                        newline: true,
 8414                    },
 8415                ],
 8416                ..Default::default()
 8417            },
 8418            autoclose_before: "})]".to_string(),
 8419            ..Default::default()
 8420        },
 8421        Some(tree_sitter_rust::LANGUAGE.into()),
 8422    );
 8423    let language = Arc::new(language);
 8424
 8425    cx.language_registry().add(language.clone());
 8426    cx.update_buffer(|buffer, cx| {
 8427        buffer.set_language(Some(language), cx);
 8428    });
 8429
 8430    // Ensure that signature_help is not called when no signature help is enabled.
 8431    cx.set_state(
 8432        &r#"
 8433            fn main() {
 8434                sampleˇ
 8435            }
 8436        "#
 8437        .unindent(),
 8438    );
 8439    cx.update_editor(|editor, window, cx| {
 8440        editor.handle_input("(", window, cx);
 8441    });
 8442    cx.assert_editor_state(
 8443        &"
 8444            fn main() {
 8445                sample(ˇ)
 8446            }
 8447        "
 8448        .unindent(),
 8449    );
 8450    cx.editor(|editor, _, _| {
 8451        assert!(editor.signature_help_state.task().is_none());
 8452    });
 8453
 8454    let mocked_response = lsp::SignatureHelp {
 8455        signatures: vec![lsp::SignatureInformation {
 8456            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8457            documentation: None,
 8458            parameters: Some(vec![
 8459                lsp::ParameterInformation {
 8460                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8461                    documentation: None,
 8462                },
 8463                lsp::ParameterInformation {
 8464                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8465                    documentation: None,
 8466                },
 8467            ]),
 8468            active_parameter: None,
 8469        }],
 8470        active_signature: Some(0),
 8471        active_parameter: Some(0),
 8472    };
 8473
 8474    // Ensure that signature_help is called when enabled afte edits
 8475    cx.update(|_, cx| {
 8476        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8477            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8478                settings.auto_signature_help = Some(false);
 8479                settings.show_signature_help_after_edits = Some(true);
 8480            });
 8481        });
 8482    });
 8483    cx.set_state(
 8484        &r#"
 8485            fn main() {
 8486                sampleˇ
 8487            }
 8488        "#
 8489        .unindent(),
 8490    );
 8491    cx.update_editor(|editor, window, cx| {
 8492        editor.handle_input("(", window, cx);
 8493    });
 8494    cx.assert_editor_state(
 8495        &"
 8496            fn main() {
 8497                sample(ˇ)
 8498            }
 8499        "
 8500        .unindent(),
 8501    );
 8502    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8503    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8504        .await;
 8505    cx.update_editor(|editor, _, _| {
 8506        let signature_help_state = editor.signature_help_state.popover().cloned();
 8507        assert!(signature_help_state.is_some());
 8508        assert_eq!(
 8509            signature_help_state.unwrap().label,
 8510            "param1: u8, param2: u8"
 8511        );
 8512        editor.signature_help_state = SignatureHelpState::default();
 8513    });
 8514
 8515    // Ensure that signature_help is called when auto signature help override is enabled
 8516    cx.update(|_, cx| {
 8517        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8518            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8519                settings.auto_signature_help = Some(true);
 8520                settings.show_signature_help_after_edits = Some(false);
 8521            });
 8522        });
 8523    });
 8524    cx.set_state(
 8525        &r#"
 8526            fn main() {
 8527                sampleˇ
 8528            }
 8529        "#
 8530        .unindent(),
 8531    );
 8532    cx.update_editor(|editor, window, cx| {
 8533        editor.handle_input("(", window, cx);
 8534    });
 8535    cx.assert_editor_state(
 8536        &"
 8537            fn main() {
 8538                sample(ˇ)
 8539            }
 8540        "
 8541        .unindent(),
 8542    );
 8543    handle_signature_help_request(&mut cx, mocked_response).await;
 8544    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8545        .await;
 8546    cx.editor(|editor, _, _| {
 8547        let signature_help_state = editor.signature_help_state.popover().cloned();
 8548        assert!(signature_help_state.is_some());
 8549        assert_eq!(
 8550            signature_help_state.unwrap().label,
 8551            "param1: u8, param2: u8"
 8552        );
 8553    });
 8554}
 8555
 8556#[gpui::test]
 8557async fn test_signature_help(cx: &mut TestAppContext) {
 8558    init_test(cx, |_| {});
 8559    cx.update(|cx| {
 8560        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8561            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8562                settings.auto_signature_help = Some(true);
 8563            });
 8564        });
 8565    });
 8566
 8567    let mut cx = EditorLspTestContext::new_rust(
 8568        lsp::ServerCapabilities {
 8569            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8570                ..Default::default()
 8571            }),
 8572            ..Default::default()
 8573        },
 8574        cx,
 8575    )
 8576    .await;
 8577
 8578    // A test that directly calls `show_signature_help`
 8579    cx.update_editor(|editor, window, cx| {
 8580        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8581    });
 8582
 8583    let mocked_response = lsp::SignatureHelp {
 8584        signatures: vec![lsp::SignatureInformation {
 8585            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8586            documentation: None,
 8587            parameters: Some(vec![
 8588                lsp::ParameterInformation {
 8589                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8590                    documentation: None,
 8591                },
 8592                lsp::ParameterInformation {
 8593                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8594                    documentation: None,
 8595                },
 8596            ]),
 8597            active_parameter: None,
 8598        }],
 8599        active_signature: Some(0),
 8600        active_parameter: Some(0),
 8601    };
 8602    handle_signature_help_request(&mut cx, mocked_response).await;
 8603
 8604    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8605        .await;
 8606
 8607    cx.editor(|editor, _, _| {
 8608        let signature_help_state = editor.signature_help_state.popover().cloned();
 8609        assert!(signature_help_state.is_some());
 8610        assert_eq!(
 8611            signature_help_state.unwrap().label,
 8612            "param1: u8, param2: u8"
 8613        );
 8614    });
 8615
 8616    // When exiting outside from inside the brackets, `signature_help` is closed.
 8617    cx.set_state(indoc! {"
 8618        fn main() {
 8619            sample(ˇ);
 8620        }
 8621
 8622        fn sample(param1: u8, param2: u8) {}
 8623    "});
 8624
 8625    cx.update_editor(|editor, window, cx| {
 8626        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8627    });
 8628
 8629    let mocked_response = lsp::SignatureHelp {
 8630        signatures: Vec::new(),
 8631        active_signature: None,
 8632        active_parameter: None,
 8633    };
 8634    handle_signature_help_request(&mut cx, mocked_response).await;
 8635
 8636    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8637        .await;
 8638
 8639    cx.editor(|editor, _, _| {
 8640        assert!(!editor.signature_help_state.is_shown());
 8641    });
 8642
 8643    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8644    cx.set_state(indoc! {"
 8645        fn main() {
 8646            sample(ˇ);
 8647        }
 8648
 8649        fn sample(param1: u8, param2: u8) {}
 8650    "});
 8651
 8652    let mocked_response = lsp::SignatureHelp {
 8653        signatures: vec![lsp::SignatureInformation {
 8654            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8655            documentation: None,
 8656            parameters: Some(vec![
 8657                lsp::ParameterInformation {
 8658                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8659                    documentation: None,
 8660                },
 8661                lsp::ParameterInformation {
 8662                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8663                    documentation: None,
 8664                },
 8665            ]),
 8666            active_parameter: None,
 8667        }],
 8668        active_signature: Some(0),
 8669        active_parameter: Some(0),
 8670    };
 8671    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8672    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8673        .await;
 8674    cx.editor(|editor, _, _| {
 8675        assert!(editor.signature_help_state.is_shown());
 8676    });
 8677
 8678    // Restore the popover with more parameter input
 8679    cx.set_state(indoc! {"
 8680        fn main() {
 8681            sample(param1, param2ˇ);
 8682        }
 8683
 8684        fn sample(param1: u8, param2: u8) {}
 8685    "});
 8686
 8687    let mocked_response = lsp::SignatureHelp {
 8688        signatures: vec![lsp::SignatureInformation {
 8689            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8690            documentation: None,
 8691            parameters: Some(vec![
 8692                lsp::ParameterInformation {
 8693                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8694                    documentation: None,
 8695                },
 8696                lsp::ParameterInformation {
 8697                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8698                    documentation: None,
 8699                },
 8700            ]),
 8701            active_parameter: None,
 8702        }],
 8703        active_signature: Some(0),
 8704        active_parameter: Some(1),
 8705    };
 8706    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8707    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8708        .await;
 8709
 8710    // When selecting a range, the popover is gone.
 8711    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8712    cx.update_editor(|editor, window, cx| {
 8713        editor.change_selections(None, window, cx, |s| {
 8714            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8715        })
 8716    });
 8717    cx.assert_editor_state(indoc! {"
 8718        fn main() {
 8719            sample(param1, «ˇparam2»);
 8720        }
 8721
 8722        fn sample(param1: u8, param2: u8) {}
 8723    "});
 8724    cx.editor(|editor, _, _| {
 8725        assert!(!editor.signature_help_state.is_shown());
 8726    });
 8727
 8728    // When unselecting again, the popover is back if within the brackets.
 8729    cx.update_editor(|editor, window, cx| {
 8730        editor.change_selections(None, window, cx, |s| {
 8731            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8732        })
 8733    });
 8734    cx.assert_editor_state(indoc! {"
 8735        fn main() {
 8736            sample(param1, ˇparam2);
 8737        }
 8738
 8739        fn sample(param1: u8, param2: u8) {}
 8740    "});
 8741    handle_signature_help_request(&mut cx, mocked_response).await;
 8742    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8743        .await;
 8744    cx.editor(|editor, _, _| {
 8745        assert!(editor.signature_help_state.is_shown());
 8746    });
 8747
 8748    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8749    cx.update_editor(|editor, window, cx| {
 8750        editor.change_selections(None, window, cx, |s| {
 8751            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8752            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8753        })
 8754    });
 8755    cx.assert_editor_state(indoc! {"
 8756        fn main() {
 8757            sample(param1, ˇparam2);
 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(1),
 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.update_editor(|editor, _, cx| {
 8786        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8787    });
 8788    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8789        .await;
 8790    cx.update_editor(|editor, window, cx| {
 8791        editor.change_selections(None, window, cx, |s| {
 8792            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8793        })
 8794    });
 8795    cx.assert_editor_state(indoc! {"
 8796        fn main() {
 8797            sample(param1, «ˇparam2»);
 8798        }
 8799
 8800        fn sample(param1: u8, param2: u8) {}
 8801    "});
 8802    cx.update_editor(|editor, window, cx| {
 8803        editor.change_selections(None, window, cx, |s| {
 8804            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8805        })
 8806    });
 8807    cx.assert_editor_state(indoc! {"
 8808        fn main() {
 8809            sample(param1, ˇparam2);
 8810        }
 8811
 8812        fn sample(param1: u8, param2: u8) {}
 8813    "});
 8814    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8815        .await;
 8816}
 8817
 8818#[gpui::test]
 8819async fn test_completion(cx: &mut TestAppContext) {
 8820    init_test(cx, |_| {});
 8821
 8822    let mut cx = EditorLspTestContext::new_rust(
 8823        lsp::ServerCapabilities {
 8824            completion_provider: Some(lsp::CompletionOptions {
 8825                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8826                resolve_provider: Some(true),
 8827                ..Default::default()
 8828            }),
 8829            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8830            ..Default::default()
 8831        },
 8832        cx,
 8833    )
 8834    .await;
 8835    let counter = Arc::new(AtomicUsize::new(0));
 8836
 8837    cx.set_state(indoc! {"
 8838        oneˇ
 8839        two
 8840        three
 8841    "});
 8842    cx.simulate_keystroke(".");
 8843    handle_completion_request(
 8844        &mut cx,
 8845        indoc! {"
 8846            one.|<>
 8847            two
 8848            three
 8849        "},
 8850        vec!["first_completion", "second_completion"],
 8851        counter.clone(),
 8852    )
 8853    .await;
 8854    cx.condition(|editor, _| editor.context_menu_visible())
 8855        .await;
 8856    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8857
 8858    let _handler = handle_signature_help_request(
 8859        &mut cx,
 8860        lsp::SignatureHelp {
 8861            signatures: vec![lsp::SignatureInformation {
 8862                label: "test signature".to_string(),
 8863                documentation: None,
 8864                parameters: Some(vec![lsp::ParameterInformation {
 8865                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8866                    documentation: None,
 8867                }]),
 8868                active_parameter: None,
 8869            }],
 8870            active_signature: None,
 8871            active_parameter: None,
 8872        },
 8873    );
 8874    cx.update_editor(|editor, window, cx| {
 8875        assert!(
 8876            !editor.signature_help_state.is_shown(),
 8877            "No signature help was called for"
 8878        );
 8879        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8880    });
 8881    cx.run_until_parked();
 8882    cx.update_editor(|editor, _, _| {
 8883        assert!(
 8884            !editor.signature_help_state.is_shown(),
 8885            "No signature help should be shown when completions menu is open"
 8886        );
 8887    });
 8888
 8889    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8890        editor.context_menu_next(&Default::default(), window, cx);
 8891        editor
 8892            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8893            .unwrap()
 8894    });
 8895    cx.assert_editor_state(indoc! {"
 8896        one.second_completionˇ
 8897        two
 8898        three
 8899    "});
 8900
 8901    handle_resolve_completion_request(
 8902        &mut cx,
 8903        Some(vec![
 8904            (
 8905                //This overlaps with the primary completion edit which is
 8906                //misbehavior from the LSP spec, test that we filter it out
 8907                indoc! {"
 8908                    one.second_ˇcompletion
 8909                    two
 8910                    threeˇ
 8911                "},
 8912                "overlapping additional edit",
 8913            ),
 8914            (
 8915                indoc! {"
 8916                    one.second_completion
 8917                    two
 8918                    threeˇ
 8919                "},
 8920                "\nadditional edit",
 8921            ),
 8922        ]),
 8923    )
 8924    .await;
 8925    apply_additional_edits.await.unwrap();
 8926    cx.assert_editor_state(indoc! {"
 8927        one.second_completionˇ
 8928        two
 8929        three
 8930        additional edit
 8931    "});
 8932
 8933    cx.set_state(indoc! {"
 8934        one.second_completion
 8935        twoˇ
 8936        threeˇ
 8937        additional edit
 8938    "});
 8939    cx.simulate_keystroke(" ");
 8940    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8941    cx.simulate_keystroke("s");
 8942    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8943
 8944    cx.assert_editor_state(indoc! {"
 8945        one.second_completion
 8946        two sˇ
 8947        three sˇ
 8948        additional edit
 8949    "});
 8950    handle_completion_request(
 8951        &mut cx,
 8952        indoc! {"
 8953            one.second_completion
 8954            two s
 8955            three <s|>
 8956            additional edit
 8957        "},
 8958        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8959        counter.clone(),
 8960    )
 8961    .await;
 8962    cx.condition(|editor, _| editor.context_menu_visible())
 8963        .await;
 8964    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8965
 8966    cx.simulate_keystroke("i");
 8967
 8968    handle_completion_request(
 8969        &mut cx,
 8970        indoc! {"
 8971            one.second_completion
 8972            two si
 8973            three <si|>
 8974            additional edit
 8975        "},
 8976        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8977        counter.clone(),
 8978    )
 8979    .await;
 8980    cx.condition(|editor, _| editor.context_menu_visible())
 8981        .await;
 8982    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8983
 8984    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8985        editor
 8986            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8987            .unwrap()
 8988    });
 8989    cx.assert_editor_state(indoc! {"
 8990        one.second_completion
 8991        two sixth_completionˇ
 8992        three sixth_completionˇ
 8993        additional edit
 8994    "});
 8995
 8996    apply_additional_edits.await.unwrap();
 8997
 8998    update_test_language_settings(&mut cx, |settings| {
 8999        settings.defaults.show_completions_on_input = Some(false);
 9000    });
 9001    cx.set_state("editorˇ");
 9002    cx.simulate_keystroke(".");
 9003    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9004    cx.simulate_keystroke("c");
 9005    cx.simulate_keystroke("l");
 9006    cx.simulate_keystroke("o");
 9007    cx.assert_editor_state("editor.cloˇ");
 9008    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9009    cx.update_editor(|editor, window, cx| {
 9010        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9011    });
 9012    handle_completion_request(
 9013        &mut cx,
 9014        "editor.<clo|>",
 9015        vec!["close", "clobber"],
 9016        counter.clone(),
 9017    )
 9018    .await;
 9019    cx.condition(|editor, _| editor.context_menu_visible())
 9020        .await;
 9021    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9022
 9023    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9024        editor
 9025            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9026            .unwrap()
 9027    });
 9028    cx.assert_editor_state("editor.closeˇ");
 9029    handle_resolve_completion_request(&mut cx, None).await;
 9030    apply_additional_edits.await.unwrap();
 9031}
 9032
 9033#[gpui::test]
 9034async fn test_multiline_completion(cx: &mut TestAppContext) {
 9035    init_test(cx, |_| {});
 9036
 9037    let fs = FakeFs::new(cx.executor());
 9038    fs.insert_tree(
 9039        path!("/a"),
 9040        json!({
 9041            "main.ts": "a",
 9042        }),
 9043    )
 9044    .await;
 9045
 9046    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9047    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9048    let typescript_language = Arc::new(Language::new(
 9049        LanguageConfig {
 9050            name: "TypeScript".into(),
 9051            matcher: LanguageMatcher {
 9052                path_suffixes: vec!["ts".to_string()],
 9053                ..LanguageMatcher::default()
 9054            },
 9055            line_comments: vec!["// ".into()],
 9056            ..LanguageConfig::default()
 9057        },
 9058        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9059    ));
 9060    language_registry.add(typescript_language.clone());
 9061    let mut fake_servers = language_registry.register_fake_lsp(
 9062        "TypeScript",
 9063        FakeLspAdapter {
 9064            capabilities: lsp::ServerCapabilities {
 9065                completion_provider: Some(lsp::CompletionOptions {
 9066                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9067                    ..lsp::CompletionOptions::default()
 9068                }),
 9069                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9070                ..lsp::ServerCapabilities::default()
 9071            },
 9072            // Emulate vtsls label generation
 9073            label_for_completion: Some(Box::new(|item, _| {
 9074                let text = if let Some(description) = item
 9075                    .label_details
 9076                    .as_ref()
 9077                    .and_then(|label_details| label_details.description.as_ref())
 9078                {
 9079                    format!("{} {}", item.label, description)
 9080                } else if let Some(detail) = &item.detail {
 9081                    format!("{} {}", item.label, detail)
 9082                } else {
 9083                    item.label.clone()
 9084                };
 9085                let len = text.len();
 9086                Some(language::CodeLabel {
 9087                    text,
 9088                    runs: Vec::new(),
 9089                    filter_range: 0..len,
 9090                })
 9091            })),
 9092            ..FakeLspAdapter::default()
 9093        },
 9094    );
 9095    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9096    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9097    let worktree_id = workspace
 9098        .update(cx, |workspace, _window, cx| {
 9099            workspace.project().update(cx, |project, cx| {
 9100                project.worktrees(cx).next().unwrap().read(cx).id()
 9101            })
 9102        })
 9103        .unwrap();
 9104    let _buffer = project
 9105        .update(cx, |project, cx| {
 9106            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9107        })
 9108        .await
 9109        .unwrap();
 9110    let editor = workspace
 9111        .update(cx, |workspace, window, cx| {
 9112            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9113        })
 9114        .unwrap()
 9115        .await
 9116        .unwrap()
 9117        .downcast::<Editor>()
 9118        .unwrap();
 9119    let fake_server = fake_servers.next().await.unwrap();
 9120
 9121    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9122    let multiline_label_2 = "a\nb\nc\n";
 9123    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9124    let multiline_description = "d\ne\nf\n";
 9125    let multiline_detail_2 = "g\nh\ni\n";
 9126
 9127    let mut completion_handle =
 9128        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9129            Ok(Some(lsp::CompletionResponse::Array(vec![
 9130                lsp::CompletionItem {
 9131                    label: multiline_label.to_string(),
 9132                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9133                        range: lsp::Range {
 9134                            start: lsp::Position {
 9135                                line: params.text_document_position.position.line,
 9136                                character: params.text_document_position.position.character,
 9137                            },
 9138                            end: lsp::Position {
 9139                                line: params.text_document_position.position.line,
 9140                                character: params.text_document_position.position.character,
 9141                            },
 9142                        },
 9143                        new_text: "new_text_1".to_string(),
 9144                    })),
 9145                    ..lsp::CompletionItem::default()
 9146                },
 9147                lsp::CompletionItem {
 9148                    label: "single line label 1".to_string(),
 9149                    detail: Some(multiline_detail.to_string()),
 9150                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9151                        range: lsp::Range {
 9152                            start: lsp::Position {
 9153                                line: params.text_document_position.position.line,
 9154                                character: params.text_document_position.position.character,
 9155                            },
 9156                            end: lsp::Position {
 9157                                line: params.text_document_position.position.line,
 9158                                character: params.text_document_position.position.character,
 9159                            },
 9160                        },
 9161                        new_text: "new_text_2".to_string(),
 9162                    })),
 9163                    ..lsp::CompletionItem::default()
 9164                },
 9165                lsp::CompletionItem {
 9166                    label: "single line label 2".to_string(),
 9167                    label_details: Some(lsp::CompletionItemLabelDetails {
 9168                        description: Some(multiline_description.to_string()),
 9169                        detail: None,
 9170                    }),
 9171                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9172                        range: lsp::Range {
 9173                            start: lsp::Position {
 9174                                line: params.text_document_position.position.line,
 9175                                character: params.text_document_position.position.character,
 9176                            },
 9177                            end: lsp::Position {
 9178                                line: params.text_document_position.position.line,
 9179                                character: params.text_document_position.position.character,
 9180                            },
 9181                        },
 9182                        new_text: "new_text_2".to_string(),
 9183                    })),
 9184                    ..lsp::CompletionItem::default()
 9185                },
 9186                lsp::CompletionItem {
 9187                    label: multiline_label_2.to_string(),
 9188                    detail: Some(multiline_detail_2.to_string()),
 9189                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9190                        range: lsp::Range {
 9191                            start: lsp::Position {
 9192                                line: params.text_document_position.position.line,
 9193                                character: params.text_document_position.position.character,
 9194                            },
 9195                            end: lsp::Position {
 9196                                line: params.text_document_position.position.line,
 9197                                character: params.text_document_position.position.character,
 9198                            },
 9199                        },
 9200                        new_text: "new_text_3".to_string(),
 9201                    })),
 9202                    ..lsp::CompletionItem::default()
 9203                },
 9204                lsp::CompletionItem {
 9205                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9206                    detail: Some(
 9207                        "Details with many     spaces and \t but without newlines".to_string(),
 9208                    ),
 9209                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9210                        range: lsp::Range {
 9211                            start: lsp::Position {
 9212                                line: params.text_document_position.position.line,
 9213                                character: params.text_document_position.position.character,
 9214                            },
 9215                            end: lsp::Position {
 9216                                line: params.text_document_position.position.line,
 9217                                character: params.text_document_position.position.character,
 9218                            },
 9219                        },
 9220                        new_text: "new_text_4".to_string(),
 9221                    })),
 9222                    ..lsp::CompletionItem::default()
 9223                },
 9224            ])))
 9225        });
 9226
 9227    editor.update_in(cx, |editor, window, cx| {
 9228        cx.focus_self(window);
 9229        editor.move_to_end(&MoveToEnd, window, cx);
 9230        editor.handle_input(".", window, cx);
 9231    });
 9232    cx.run_until_parked();
 9233    completion_handle.next().await.unwrap();
 9234
 9235    editor.update(cx, |editor, _| {
 9236        assert!(editor.context_menu_visible());
 9237        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9238        {
 9239            let completion_labels = menu
 9240                .completions
 9241                .borrow()
 9242                .iter()
 9243                .map(|c| c.label.text.clone())
 9244                .collect::<Vec<_>>();
 9245            assert_eq!(
 9246                completion_labels,
 9247                &[
 9248                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9249                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9250                    "single line label 2 d e f ",
 9251                    "a b c g h i ",
 9252                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9253                ],
 9254                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9255            );
 9256
 9257            for completion in menu
 9258                .completions
 9259                .borrow()
 9260                .iter() {
 9261                    assert_eq!(
 9262                        completion.label.filter_range,
 9263                        0..completion.label.text.len(),
 9264                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9265                    );
 9266                }
 9267
 9268        } else {
 9269            panic!("expected completion menu to be open");
 9270        }
 9271    });
 9272}
 9273
 9274#[gpui::test]
 9275async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9276    init_test(cx, |_| {});
 9277    let mut cx = EditorLspTestContext::new_rust(
 9278        lsp::ServerCapabilities {
 9279            completion_provider: Some(lsp::CompletionOptions {
 9280                trigger_characters: Some(vec![".".to_string()]),
 9281                ..Default::default()
 9282            }),
 9283            ..Default::default()
 9284        },
 9285        cx,
 9286    )
 9287    .await;
 9288    cx.lsp
 9289        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9290            Ok(Some(lsp::CompletionResponse::Array(vec![
 9291                lsp::CompletionItem {
 9292                    label: "first".into(),
 9293                    ..Default::default()
 9294                },
 9295                lsp::CompletionItem {
 9296                    label: "last".into(),
 9297                    ..Default::default()
 9298                },
 9299            ])))
 9300        });
 9301    cx.set_state("variableˇ");
 9302    cx.simulate_keystroke(".");
 9303    cx.executor().run_until_parked();
 9304
 9305    cx.update_editor(|editor, _, _| {
 9306        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9307        {
 9308            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9309        } else {
 9310            panic!("expected completion menu to be open");
 9311        }
 9312    });
 9313
 9314    cx.update_editor(|editor, window, cx| {
 9315        editor.move_page_down(&MovePageDown::default(), window, cx);
 9316        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9317        {
 9318            assert!(
 9319                menu.selected_item == 1,
 9320                "expected PageDown to select the last item from the context menu"
 9321            );
 9322        } else {
 9323            panic!("expected completion menu to stay open after PageDown");
 9324        }
 9325    });
 9326
 9327    cx.update_editor(|editor, window, cx| {
 9328        editor.move_page_up(&MovePageUp::default(), window, cx);
 9329        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9330        {
 9331            assert!(
 9332                menu.selected_item == 0,
 9333                "expected PageUp to select the first item from the context menu"
 9334            );
 9335        } else {
 9336            panic!("expected completion menu to stay open after PageUp");
 9337        }
 9338    });
 9339}
 9340
 9341#[gpui::test]
 9342async fn test_completion_sort(cx: &mut TestAppContext) {
 9343    init_test(cx, |_| {});
 9344    let mut cx = EditorLspTestContext::new_rust(
 9345        lsp::ServerCapabilities {
 9346            completion_provider: Some(lsp::CompletionOptions {
 9347                trigger_characters: Some(vec![".".to_string()]),
 9348                ..Default::default()
 9349            }),
 9350            ..Default::default()
 9351        },
 9352        cx,
 9353    )
 9354    .await;
 9355    cx.lsp
 9356        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9357            Ok(Some(lsp::CompletionResponse::Array(vec![
 9358                lsp::CompletionItem {
 9359                    label: "Range".into(),
 9360                    sort_text: Some("a".into()),
 9361                    ..Default::default()
 9362                },
 9363                lsp::CompletionItem {
 9364                    label: "r".into(),
 9365                    sort_text: Some("b".into()),
 9366                    ..Default::default()
 9367                },
 9368                lsp::CompletionItem {
 9369                    label: "ret".into(),
 9370                    sort_text: Some("c".into()),
 9371                    ..Default::default()
 9372                },
 9373                lsp::CompletionItem {
 9374                    label: "return".into(),
 9375                    sort_text: Some("d".into()),
 9376                    ..Default::default()
 9377                },
 9378                lsp::CompletionItem {
 9379                    label: "slice".into(),
 9380                    sort_text: Some("d".into()),
 9381                    ..Default::default()
 9382                },
 9383            ])))
 9384        });
 9385    cx.set_state("");
 9386    cx.executor().run_until_parked();
 9387    cx.update_editor(|editor, window, cx| {
 9388        editor.show_completions(
 9389            &ShowCompletions {
 9390                trigger: Some("r".into()),
 9391            },
 9392            window,
 9393            cx,
 9394        );
 9395    });
 9396    cx.executor().run_until_parked();
 9397
 9398    cx.update_editor(|editor, _, _| {
 9399        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9400        {
 9401            assert_eq!(
 9402                completion_menu_entries(&menu),
 9403                &["r", "ret", "Range", "return"]
 9404            );
 9405        } else {
 9406            panic!("expected completion menu to be open");
 9407        }
 9408    });
 9409}
 9410
 9411#[gpui::test]
 9412async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9413    init_test(cx, |_| {});
 9414
 9415    let mut cx = EditorLspTestContext::new_rust(
 9416        lsp::ServerCapabilities {
 9417            completion_provider: Some(lsp::CompletionOptions {
 9418                trigger_characters: Some(vec![".".to_string()]),
 9419                resolve_provider: Some(true),
 9420                ..Default::default()
 9421            }),
 9422            ..Default::default()
 9423        },
 9424        cx,
 9425    )
 9426    .await;
 9427
 9428    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9429    cx.simulate_keystroke(".");
 9430    let completion_item = lsp::CompletionItem {
 9431        label: "Some".into(),
 9432        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9433        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9434        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9435            kind: lsp::MarkupKind::Markdown,
 9436            value: "```rust\nSome(2)\n```".to_string(),
 9437        })),
 9438        deprecated: Some(false),
 9439        sort_text: Some("Some".to_string()),
 9440        filter_text: Some("Some".to_string()),
 9441        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9442        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9443            range: lsp::Range {
 9444                start: lsp::Position {
 9445                    line: 0,
 9446                    character: 22,
 9447                },
 9448                end: lsp::Position {
 9449                    line: 0,
 9450                    character: 22,
 9451                },
 9452            },
 9453            new_text: "Some(2)".to_string(),
 9454        })),
 9455        additional_text_edits: Some(vec![lsp::TextEdit {
 9456            range: lsp::Range {
 9457                start: lsp::Position {
 9458                    line: 0,
 9459                    character: 20,
 9460                },
 9461                end: lsp::Position {
 9462                    line: 0,
 9463                    character: 22,
 9464                },
 9465            },
 9466            new_text: "".to_string(),
 9467        }]),
 9468        ..Default::default()
 9469    };
 9470
 9471    let closure_completion_item = completion_item.clone();
 9472    let counter = Arc::new(AtomicUsize::new(0));
 9473    let counter_clone = counter.clone();
 9474    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9475        let task_completion_item = closure_completion_item.clone();
 9476        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9477        async move {
 9478            Ok(Some(lsp::CompletionResponse::Array(vec![
 9479                task_completion_item,
 9480            ])))
 9481        }
 9482    });
 9483
 9484    cx.condition(|editor, _| editor.context_menu_visible())
 9485        .await;
 9486    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9487    assert!(request.next().await.is_some());
 9488    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9489
 9490    cx.simulate_keystroke("S");
 9491    cx.simulate_keystroke("o");
 9492    cx.simulate_keystroke("m");
 9493    cx.condition(|editor, _| editor.context_menu_visible())
 9494        .await;
 9495    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9496    assert!(request.next().await.is_some());
 9497    assert!(request.next().await.is_some());
 9498    assert!(request.next().await.is_some());
 9499    request.close();
 9500    assert!(request.next().await.is_none());
 9501    assert_eq!(
 9502        counter.load(atomic::Ordering::Acquire),
 9503        4,
 9504        "With the completions menu open, only one LSP request should happen per input"
 9505    );
 9506}
 9507
 9508#[gpui::test]
 9509async fn test_toggle_comment(cx: &mut TestAppContext) {
 9510    init_test(cx, |_| {});
 9511    let mut cx = EditorTestContext::new(cx).await;
 9512    let language = Arc::new(Language::new(
 9513        LanguageConfig {
 9514            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9515            ..Default::default()
 9516        },
 9517        Some(tree_sitter_rust::LANGUAGE.into()),
 9518    ));
 9519    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9520
 9521    // If multiple selections intersect a line, the line is only toggled once.
 9522    cx.set_state(indoc! {"
 9523        fn a() {
 9524            «//b();
 9525            ˇ»// «c();
 9526            //ˇ»  d();
 9527        }
 9528    "});
 9529
 9530    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9531
 9532    cx.assert_editor_state(indoc! {"
 9533        fn a() {
 9534            «b();
 9535            c();
 9536            ˇ» d();
 9537        }
 9538    "});
 9539
 9540    // The comment prefix is inserted at the same column for every line in a
 9541    // selection.
 9542    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9543
 9544    cx.assert_editor_state(indoc! {"
 9545        fn a() {
 9546            // «b();
 9547            // c();
 9548            ˇ»//  d();
 9549        }
 9550    "});
 9551
 9552    // If a selection ends at the beginning of a line, that line is not toggled.
 9553    cx.set_selections_state(indoc! {"
 9554        fn a() {
 9555            // b();
 9556            «// c();
 9557        ˇ»    //  d();
 9558        }
 9559    "});
 9560
 9561    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9562
 9563    cx.assert_editor_state(indoc! {"
 9564        fn a() {
 9565            // b();
 9566            «c();
 9567        ˇ»    //  d();
 9568        }
 9569    "});
 9570
 9571    // If a selection span a single line and is empty, the line is toggled.
 9572    cx.set_state(indoc! {"
 9573        fn a() {
 9574            a();
 9575            b();
 9576        ˇ
 9577        }
 9578    "});
 9579
 9580    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9581
 9582    cx.assert_editor_state(indoc! {"
 9583        fn a() {
 9584            a();
 9585            b();
 9586        //•ˇ
 9587        }
 9588    "});
 9589
 9590    // If a selection span multiple lines, empty lines are not toggled.
 9591    cx.set_state(indoc! {"
 9592        fn a() {
 9593            «a();
 9594
 9595            c();ˇ»
 9596        }
 9597    "});
 9598
 9599    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9600
 9601    cx.assert_editor_state(indoc! {"
 9602        fn a() {
 9603            // «a();
 9604
 9605            // c();ˇ»
 9606        }
 9607    "});
 9608
 9609    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9610    cx.set_state(indoc! {"
 9611        fn a() {
 9612            «// a();
 9613            /// b();
 9614            //! c();ˇ»
 9615        }
 9616    "});
 9617
 9618    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9619
 9620    cx.assert_editor_state(indoc! {"
 9621        fn a() {
 9622            «a();
 9623            b();
 9624            c();ˇ»
 9625        }
 9626    "});
 9627}
 9628
 9629#[gpui::test]
 9630async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9631    init_test(cx, |_| {});
 9632    let mut cx = EditorTestContext::new(cx).await;
 9633    let language = Arc::new(Language::new(
 9634        LanguageConfig {
 9635            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9636            ..Default::default()
 9637        },
 9638        Some(tree_sitter_rust::LANGUAGE.into()),
 9639    ));
 9640    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9641
 9642    let toggle_comments = &ToggleComments {
 9643        advance_downwards: false,
 9644        ignore_indent: true,
 9645    };
 9646
 9647    // If multiple selections intersect a line, the line is only toggled once.
 9648    cx.set_state(indoc! {"
 9649        fn a() {
 9650        //    «b();
 9651        //    c();
 9652        //    ˇ» d();
 9653        }
 9654    "});
 9655
 9656    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9657
 9658    cx.assert_editor_state(indoc! {"
 9659        fn a() {
 9660            «b();
 9661            c();
 9662            ˇ» d();
 9663        }
 9664    "});
 9665
 9666    // The comment prefix is inserted at the beginning of each line
 9667    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9668
 9669    cx.assert_editor_state(indoc! {"
 9670        fn a() {
 9671        //    «b();
 9672        //    c();
 9673        //    ˇ» d();
 9674        }
 9675    "});
 9676
 9677    // If a selection ends at the beginning of a line, that line is not toggled.
 9678    cx.set_selections_state(indoc! {"
 9679        fn a() {
 9680        //    b();
 9681        //    «c();
 9682        ˇ»//     d();
 9683        }
 9684    "});
 9685
 9686    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9687
 9688    cx.assert_editor_state(indoc! {"
 9689        fn a() {
 9690        //    b();
 9691            «c();
 9692        ˇ»//     d();
 9693        }
 9694    "});
 9695
 9696    // If a selection span a single line and is empty, the line is toggled.
 9697    cx.set_state(indoc! {"
 9698        fn a() {
 9699            a();
 9700            b();
 9701        ˇ
 9702        }
 9703    "});
 9704
 9705    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9706
 9707    cx.assert_editor_state(indoc! {"
 9708        fn a() {
 9709            a();
 9710            b();
 9711        //ˇ
 9712        }
 9713    "});
 9714
 9715    // If a selection span multiple lines, empty lines are not toggled.
 9716    cx.set_state(indoc! {"
 9717        fn a() {
 9718            «a();
 9719
 9720            c();ˇ»
 9721        }
 9722    "});
 9723
 9724    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9725
 9726    cx.assert_editor_state(indoc! {"
 9727        fn a() {
 9728        //    «a();
 9729
 9730        //    c();ˇ»
 9731        }
 9732    "});
 9733
 9734    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9735    cx.set_state(indoc! {"
 9736        fn a() {
 9737        //    «a();
 9738        ///    b();
 9739        //!    c();ˇ»
 9740        }
 9741    "});
 9742
 9743    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9744
 9745    cx.assert_editor_state(indoc! {"
 9746        fn a() {
 9747            «a();
 9748            b();
 9749            c();ˇ»
 9750        }
 9751    "});
 9752}
 9753
 9754#[gpui::test]
 9755async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9756    init_test(cx, |_| {});
 9757
 9758    let language = Arc::new(Language::new(
 9759        LanguageConfig {
 9760            line_comments: vec!["// ".into()],
 9761            ..Default::default()
 9762        },
 9763        Some(tree_sitter_rust::LANGUAGE.into()),
 9764    ));
 9765
 9766    let mut cx = EditorTestContext::new(cx).await;
 9767
 9768    cx.language_registry().add(language.clone());
 9769    cx.update_buffer(|buffer, cx| {
 9770        buffer.set_language(Some(language), cx);
 9771    });
 9772
 9773    let toggle_comments = &ToggleComments {
 9774        advance_downwards: true,
 9775        ignore_indent: false,
 9776    };
 9777
 9778    // Single cursor on one line -> advance
 9779    // Cursor moves horizontally 3 characters as well on non-blank line
 9780    cx.set_state(indoc!(
 9781        "fn a() {
 9782             ˇdog();
 9783             cat();
 9784        }"
 9785    ));
 9786    cx.update_editor(|editor, window, cx| {
 9787        editor.toggle_comments(toggle_comments, window, cx);
 9788    });
 9789    cx.assert_editor_state(indoc!(
 9790        "fn a() {
 9791             // dog();
 9792             catˇ();
 9793        }"
 9794    ));
 9795
 9796    // Single selection on one line -> don't advance
 9797    cx.set_state(indoc!(
 9798        "fn a() {
 9799             «dog()ˇ»;
 9800             cat();
 9801        }"
 9802    ));
 9803    cx.update_editor(|editor, window, cx| {
 9804        editor.toggle_comments(toggle_comments, window, cx);
 9805    });
 9806    cx.assert_editor_state(indoc!(
 9807        "fn a() {
 9808             // «dog()ˇ»;
 9809             cat();
 9810        }"
 9811    ));
 9812
 9813    // Multiple cursors on one line -> advance
 9814    cx.set_state(indoc!(
 9815        "fn a() {
 9816             ˇdˇog();
 9817             cat();
 9818        }"
 9819    ));
 9820    cx.update_editor(|editor, window, cx| {
 9821        editor.toggle_comments(toggle_comments, window, cx);
 9822    });
 9823    cx.assert_editor_state(indoc!(
 9824        "fn a() {
 9825             // dog();
 9826             catˇ(ˇ);
 9827        }"
 9828    ));
 9829
 9830    // Multiple cursors on one line, with selection -> don't advance
 9831    cx.set_state(indoc!(
 9832        "fn a() {
 9833             ˇdˇog«()ˇ»;
 9834             cat();
 9835        }"
 9836    ));
 9837    cx.update_editor(|editor, window, cx| {
 9838        editor.toggle_comments(toggle_comments, window, cx);
 9839    });
 9840    cx.assert_editor_state(indoc!(
 9841        "fn a() {
 9842             // ˇdˇog«()ˇ»;
 9843             cat();
 9844        }"
 9845    ));
 9846
 9847    // Single cursor on one line -> advance
 9848    // Cursor moves to column 0 on blank line
 9849    cx.set_state(indoc!(
 9850        "fn a() {
 9851             ˇdog();
 9852
 9853             cat();
 9854        }"
 9855    ));
 9856    cx.update_editor(|editor, window, cx| {
 9857        editor.toggle_comments(toggle_comments, window, cx);
 9858    });
 9859    cx.assert_editor_state(indoc!(
 9860        "fn a() {
 9861             // dog();
 9862        ˇ
 9863             cat();
 9864        }"
 9865    ));
 9866
 9867    // Single cursor on one line -> advance
 9868    // Cursor starts and ends at column 0
 9869    cx.set_state(indoc!(
 9870        "fn a() {
 9871         ˇ    dog();
 9872             cat();
 9873        }"
 9874    ));
 9875    cx.update_editor(|editor, window, cx| {
 9876        editor.toggle_comments(toggle_comments, window, cx);
 9877    });
 9878    cx.assert_editor_state(indoc!(
 9879        "fn a() {
 9880             // dog();
 9881         ˇ    cat();
 9882        }"
 9883    ));
 9884}
 9885
 9886#[gpui::test]
 9887async fn test_toggle_block_comment(cx: &mut TestAppContext) {
 9888    init_test(cx, |_| {});
 9889
 9890    let mut cx = EditorTestContext::new(cx).await;
 9891
 9892    let html_language = Arc::new(
 9893        Language::new(
 9894            LanguageConfig {
 9895                name: "HTML".into(),
 9896                block_comment: Some(("<!-- ".into(), " -->".into())),
 9897                ..Default::default()
 9898            },
 9899            Some(tree_sitter_html::LANGUAGE.into()),
 9900        )
 9901        .with_injection_query(
 9902            r#"
 9903            (script_element
 9904                (raw_text) @injection.content
 9905                (#set! injection.language "javascript"))
 9906            "#,
 9907        )
 9908        .unwrap(),
 9909    );
 9910
 9911    let javascript_language = Arc::new(Language::new(
 9912        LanguageConfig {
 9913            name: "JavaScript".into(),
 9914            line_comments: vec!["// ".into()],
 9915            ..Default::default()
 9916        },
 9917        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9918    ));
 9919
 9920    cx.language_registry().add(html_language.clone());
 9921    cx.language_registry().add(javascript_language.clone());
 9922    cx.update_buffer(|buffer, cx| {
 9923        buffer.set_language(Some(html_language), cx);
 9924    });
 9925
 9926    // Toggle comments for empty selections
 9927    cx.set_state(
 9928        &r#"
 9929            <p>A</p>ˇ
 9930            <p>B</p>ˇ
 9931            <p>C</p>ˇ
 9932        "#
 9933        .unindent(),
 9934    );
 9935    cx.update_editor(|editor, window, cx| {
 9936        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9937    });
 9938    cx.assert_editor_state(
 9939        &r#"
 9940            <!-- <p>A</p>ˇ -->
 9941            <!-- <p>B</p>ˇ -->
 9942            <!-- <p>C</p>ˇ -->
 9943        "#
 9944        .unindent(),
 9945    );
 9946    cx.update_editor(|editor, window, cx| {
 9947        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9948    });
 9949    cx.assert_editor_state(
 9950        &r#"
 9951            <p>A</p>ˇ
 9952            <p>B</p>ˇ
 9953            <p>C</p>ˇ
 9954        "#
 9955        .unindent(),
 9956    );
 9957
 9958    // Toggle comments for mixture of empty and non-empty selections, where
 9959    // multiple selections occupy a given line.
 9960    cx.set_state(
 9961        &r#"
 9962            <p>A«</p>
 9963            <p>ˇ»B</p>ˇ
 9964            <p>C«</p>
 9965            <p>ˇ»D</p>ˇ
 9966        "#
 9967        .unindent(),
 9968    );
 9969
 9970    cx.update_editor(|editor, window, cx| {
 9971        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9972    });
 9973    cx.assert_editor_state(
 9974        &r#"
 9975            <!-- <p>A«</p>
 9976            <p>ˇ»B</p>ˇ -->
 9977            <!-- <p>C«</p>
 9978            <p>ˇ»D</p>ˇ -->
 9979        "#
 9980        .unindent(),
 9981    );
 9982    cx.update_editor(|editor, window, cx| {
 9983        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9984    });
 9985    cx.assert_editor_state(
 9986        &r#"
 9987            <p>A«</p>
 9988            <p>ˇ»B</p>ˇ
 9989            <p>C«</p>
 9990            <p>ˇ»D</p>ˇ
 9991        "#
 9992        .unindent(),
 9993    );
 9994
 9995    // Toggle comments when different languages are active for different
 9996    // selections.
 9997    cx.set_state(
 9998        &r#"
 9999            ˇ<script>
10000                ˇvar x = new Y();
10001            ˇ</script>
10002        "#
10003        .unindent(),
10004    );
10005    cx.executor().run_until_parked();
10006    cx.update_editor(|editor, window, cx| {
10007        editor.toggle_comments(&ToggleComments::default(), window, cx)
10008    });
10009    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10010    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10011    cx.assert_editor_state(
10012        &r#"
10013            <!-- ˇ<script> -->
10014                // ˇvar x = new Y();
10015            <!-- ˇ</script> -->
10016        "#
10017        .unindent(),
10018    );
10019}
10020
10021#[gpui::test]
10022fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10023    init_test(cx, |_| {});
10024
10025    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10026    let multibuffer = cx.new(|cx| {
10027        let mut multibuffer = MultiBuffer::new(ReadWrite);
10028        multibuffer.push_excerpts(
10029            buffer.clone(),
10030            [
10031                ExcerptRange {
10032                    context: Point::new(0, 0)..Point::new(0, 4),
10033                    primary: None,
10034                },
10035                ExcerptRange {
10036                    context: Point::new(1, 0)..Point::new(1, 4),
10037                    primary: None,
10038                },
10039            ],
10040            cx,
10041        );
10042        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10043        multibuffer
10044    });
10045
10046    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10047    editor.update_in(cx, |editor, window, cx| {
10048        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10049        editor.change_selections(None, window, cx, |s| {
10050            s.select_ranges([
10051                Point::new(0, 0)..Point::new(0, 0),
10052                Point::new(1, 0)..Point::new(1, 0),
10053            ])
10054        });
10055
10056        editor.handle_input("X", window, cx);
10057        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10058        assert_eq!(
10059            editor.selections.ranges(cx),
10060            [
10061                Point::new(0, 1)..Point::new(0, 1),
10062                Point::new(1, 1)..Point::new(1, 1),
10063            ]
10064        );
10065
10066        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10067        editor.change_selections(None, window, cx, |s| {
10068            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10069        });
10070        editor.backspace(&Default::default(), window, cx);
10071        assert_eq!(editor.text(cx), "Xa\nbbb");
10072        assert_eq!(
10073            editor.selections.ranges(cx),
10074            [Point::new(1, 0)..Point::new(1, 0)]
10075        );
10076
10077        editor.change_selections(None, window, cx, |s| {
10078            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10079        });
10080        editor.backspace(&Default::default(), window, cx);
10081        assert_eq!(editor.text(cx), "X\nbb");
10082        assert_eq!(
10083            editor.selections.ranges(cx),
10084            [Point::new(0, 1)..Point::new(0, 1)]
10085        );
10086    });
10087}
10088
10089#[gpui::test]
10090fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10091    init_test(cx, |_| {});
10092
10093    let markers = vec![('[', ']').into(), ('(', ')').into()];
10094    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10095        indoc! {"
10096            [aaaa
10097            (bbbb]
10098            cccc)",
10099        },
10100        markers.clone(),
10101    );
10102    let excerpt_ranges = markers.into_iter().map(|marker| {
10103        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10104        ExcerptRange {
10105            context,
10106            primary: None,
10107        }
10108    });
10109    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10110    let multibuffer = cx.new(|cx| {
10111        let mut multibuffer = MultiBuffer::new(ReadWrite);
10112        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10113        multibuffer
10114    });
10115
10116    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10117    editor.update_in(cx, |editor, window, cx| {
10118        let (expected_text, selection_ranges) = marked_text_ranges(
10119            indoc! {"
10120                aaaa
10121                bˇbbb
10122                bˇbbˇb
10123                cccc"
10124            },
10125            true,
10126        );
10127        assert_eq!(editor.text(cx), expected_text);
10128        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10129
10130        editor.handle_input("X", window, cx);
10131
10132        let (expected_text, expected_selections) = marked_text_ranges(
10133            indoc! {"
10134                aaaa
10135                bXˇbbXb
10136                bXˇbbXˇb
10137                cccc"
10138            },
10139            false,
10140        );
10141        assert_eq!(editor.text(cx), expected_text);
10142        assert_eq!(editor.selections.ranges(cx), expected_selections);
10143
10144        editor.newline(&Newline, window, cx);
10145        let (expected_text, expected_selections) = marked_text_ranges(
10146            indoc! {"
10147                aaaa
10148                bX
10149                ˇbbX
10150                b
10151                bX
10152                ˇbbX
10153                ˇb
10154                cccc"
10155            },
10156            false,
10157        );
10158        assert_eq!(editor.text(cx), expected_text);
10159        assert_eq!(editor.selections.ranges(cx), expected_selections);
10160    });
10161}
10162
10163#[gpui::test]
10164fn test_refresh_selections(cx: &mut TestAppContext) {
10165    init_test(cx, |_| {});
10166
10167    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10168    let mut excerpt1_id = None;
10169    let multibuffer = cx.new(|cx| {
10170        let mut multibuffer = MultiBuffer::new(ReadWrite);
10171        excerpt1_id = multibuffer
10172            .push_excerpts(
10173                buffer.clone(),
10174                [
10175                    ExcerptRange {
10176                        context: Point::new(0, 0)..Point::new(1, 4),
10177                        primary: None,
10178                    },
10179                    ExcerptRange {
10180                        context: Point::new(1, 0)..Point::new(2, 4),
10181                        primary: None,
10182                    },
10183                ],
10184                cx,
10185            )
10186            .into_iter()
10187            .next();
10188        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10189        multibuffer
10190    });
10191
10192    let editor = cx.add_window(|window, cx| {
10193        let mut editor = build_editor(multibuffer.clone(), window, cx);
10194        let snapshot = editor.snapshot(window, cx);
10195        editor.change_selections(None, window, cx, |s| {
10196            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10197        });
10198        editor.begin_selection(
10199            Point::new(2, 1).to_display_point(&snapshot),
10200            true,
10201            1,
10202            window,
10203            cx,
10204        );
10205        assert_eq!(
10206            editor.selections.ranges(cx),
10207            [
10208                Point::new(1, 3)..Point::new(1, 3),
10209                Point::new(2, 1)..Point::new(2, 1),
10210            ]
10211        );
10212        editor
10213    });
10214
10215    // Refreshing selections is a no-op when excerpts haven't changed.
10216    _ = editor.update(cx, |editor, window, cx| {
10217        editor.change_selections(None, window, cx, |s| s.refresh());
10218        assert_eq!(
10219            editor.selections.ranges(cx),
10220            [
10221                Point::new(1, 3)..Point::new(1, 3),
10222                Point::new(2, 1)..Point::new(2, 1),
10223            ]
10224        );
10225    });
10226
10227    multibuffer.update(cx, |multibuffer, cx| {
10228        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10229    });
10230    _ = editor.update(cx, |editor, window, cx| {
10231        // Removing an excerpt causes the first selection to become degenerate.
10232        assert_eq!(
10233            editor.selections.ranges(cx),
10234            [
10235                Point::new(0, 0)..Point::new(0, 0),
10236                Point::new(0, 1)..Point::new(0, 1)
10237            ]
10238        );
10239
10240        // Refreshing selections will relocate the first selection to the original buffer
10241        // location.
10242        editor.change_selections(None, window, cx, |s| s.refresh());
10243        assert_eq!(
10244            editor.selections.ranges(cx),
10245            [
10246                Point::new(0, 1)..Point::new(0, 1),
10247                Point::new(0, 3)..Point::new(0, 3)
10248            ]
10249        );
10250        assert!(editor.selections.pending_anchor().is_some());
10251    });
10252}
10253
10254#[gpui::test]
10255fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10256    init_test(cx, |_| {});
10257
10258    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10259    let mut excerpt1_id = None;
10260    let multibuffer = cx.new(|cx| {
10261        let mut multibuffer = MultiBuffer::new(ReadWrite);
10262        excerpt1_id = multibuffer
10263            .push_excerpts(
10264                buffer.clone(),
10265                [
10266                    ExcerptRange {
10267                        context: Point::new(0, 0)..Point::new(1, 4),
10268                        primary: None,
10269                    },
10270                    ExcerptRange {
10271                        context: Point::new(1, 0)..Point::new(2, 4),
10272                        primary: None,
10273                    },
10274                ],
10275                cx,
10276            )
10277            .into_iter()
10278            .next();
10279        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10280        multibuffer
10281    });
10282
10283    let editor = cx.add_window(|window, cx| {
10284        let mut editor = build_editor(multibuffer.clone(), window, cx);
10285        let snapshot = editor.snapshot(window, cx);
10286        editor.begin_selection(
10287            Point::new(1, 3).to_display_point(&snapshot),
10288            false,
10289            1,
10290            window,
10291            cx,
10292        );
10293        assert_eq!(
10294            editor.selections.ranges(cx),
10295            [Point::new(1, 3)..Point::new(1, 3)]
10296        );
10297        editor
10298    });
10299
10300    multibuffer.update(cx, |multibuffer, cx| {
10301        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10302    });
10303    _ = editor.update(cx, |editor, window, cx| {
10304        assert_eq!(
10305            editor.selections.ranges(cx),
10306            [Point::new(0, 0)..Point::new(0, 0)]
10307        );
10308
10309        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10310        editor.change_selections(None, window, cx, |s| s.refresh());
10311        assert_eq!(
10312            editor.selections.ranges(cx),
10313            [Point::new(0, 3)..Point::new(0, 3)]
10314        );
10315        assert!(editor.selections.pending_anchor().is_some());
10316    });
10317}
10318
10319#[gpui::test]
10320async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10321    init_test(cx, |_| {});
10322
10323    let language = Arc::new(
10324        Language::new(
10325            LanguageConfig {
10326                brackets: BracketPairConfig {
10327                    pairs: vec![
10328                        BracketPair {
10329                            start: "{".to_string(),
10330                            end: "}".to_string(),
10331                            close: true,
10332                            surround: true,
10333                            newline: true,
10334                        },
10335                        BracketPair {
10336                            start: "/* ".to_string(),
10337                            end: " */".to_string(),
10338                            close: true,
10339                            surround: true,
10340                            newline: true,
10341                        },
10342                    ],
10343                    ..Default::default()
10344                },
10345                ..Default::default()
10346            },
10347            Some(tree_sitter_rust::LANGUAGE.into()),
10348        )
10349        .with_indents_query("")
10350        .unwrap(),
10351    );
10352
10353    let text = concat!(
10354        "{   }\n",     //
10355        "  x\n",       //
10356        "  /*   */\n", //
10357        "x\n",         //
10358        "{{} }\n",     //
10359    );
10360
10361    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10362    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10363    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10364    editor
10365        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10366        .await;
10367
10368    editor.update_in(cx, |editor, window, cx| {
10369        editor.change_selections(None, window, cx, |s| {
10370            s.select_display_ranges([
10371                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10372                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10373                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10374            ])
10375        });
10376        editor.newline(&Newline, window, cx);
10377
10378        assert_eq!(
10379            editor.buffer().read(cx).read(cx).text(),
10380            concat!(
10381                "{ \n",    // Suppress rustfmt
10382                "\n",      //
10383                "}\n",     //
10384                "  x\n",   //
10385                "  /* \n", //
10386                "  \n",    //
10387                "  */\n",  //
10388                "x\n",     //
10389                "{{} \n",  //
10390                "}\n",     //
10391            )
10392        );
10393    });
10394}
10395
10396#[gpui::test]
10397fn test_highlighted_ranges(cx: &mut TestAppContext) {
10398    init_test(cx, |_| {});
10399
10400    let editor = cx.add_window(|window, cx| {
10401        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10402        build_editor(buffer.clone(), window, cx)
10403    });
10404
10405    _ = editor.update(cx, |editor, window, cx| {
10406        struct Type1;
10407        struct Type2;
10408
10409        let buffer = editor.buffer.read(cx).snapshot(cx);
10410
10411        let anchor_range =
10412            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10413
10414        editor.highlight_background::<Type1>(
10415            &[
10416                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10417                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10418                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10419                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10420            ],
10421            |_| Hsla::red(),
10422            cx,
10423        );
10424        editor.highlight_background::<Type2>(
10425            &[
10426                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10427                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10428                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10429                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10430            ],
10431            |_| Hsla::green(),
10432            cx,
10433        );
10434
10435        let snapshot = editor.snapshot(window, cx);
10436        let mut highlighted_ranges = editor.background_highlights_in_range(
10437            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10438            &snapshot,
10439            cx.theme().colors(),
10440        );
10441        // Enforce a consistent ordering based on color without relying on the ordering of the
10442        // highlight's `TypeId` which is non-executor.
10443        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10444        assert_eq!(
10445            highlighted_ranges,
10446            &[
10447                (
10448                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10449                    Hsla::red(),
10450                ),
10451                (
10452                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10453                    Hsla::red(),
10454                ),
10455                (
10456                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10457                    Hsla::green(),
10458                ),
10459                (
10460                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10461                    Hsla::green(),
10462                ),
10463            ]
10464        );
10465        assert_eq!(
10466            editor.background_highlights_in_range(
10467                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10468                &snapshot,
10469                cx.theme().colors(),
10470            ),
10471            &[(
10472                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10473                Hsla::red(),
10474            )]
10475        );
10476    });
10477}
10478
10479#[gpui::test]
10480async fn test_following(cx: &mut TestAppContext) {
10481    init_test(cx, |_| {});
10482
10483    let fs = FakeFs::new(cx.executor());
10484    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10485
10486    let buffer = project.update(cx, |project, cx| {
10487        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10488        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10489    });
10490    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10491    let follower = cx.update(|cx| {
10492        cx.open_window(
10493            WindowOptions {
10494                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10495                    gpui::Point::new(px(0.), px(0.)),
10496                    gpui::Point::new(px(10.), px(80.)),
10497                ))),
10498                ..Default::default()
10499            },
10500            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10501        )
10502        .unwrap()
10503    });
10504
10505    let is_still_following = Rc::new(RefCell::new(true));
10506    let follower_edit_event_count = Rc::new(RefCell::new(0));
10507    let pending_update = Rc::new(RefCell::new(None));
10508    let leader_entity = leader.root(cx).unwrap();
10509    let follower_entity = follower.root(cx).unwrap();
10510    _ = follower.update(cx, {
10511        let update = pending_update.clone();
10512        let is_still_following = is_still_following.clone();
10513        let follower_edit_event_count = follower_edit_event_count.clone();
10514        |_, window, cx| {
10515            cx.subscribe_in(
10516                &leader_entity,
10517                window,
10518                move |_, leader, event, window, cx| {
10519                    leader.read(cx).add_event_to_update_proto(
10520                        event,
10521                        &mut update.borrow_mut(),
10522                        window,
10523                        cx,
10524                    );
10525                },
10526            )
10527            .detach();
10528
10529            cx.subscribe_in(
10530                &follower_entity,
10531                window,
10532                move |_, _, event: &EditorEvent, _window, _cx| {
10533                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10534                        *is_still_following.borrow_mut() = false;
10535                    }
10536
10537                    if let EditorEvent::BufferEdited = event {
10538                        *follower_edit_event_count.borrow_mut() += 1;
10539                    }
10540                },
10541            )
10542            .detach();
10543        }
10544    });
10545
10546    // Update the selections only
10547    _ = leader.update(cx, |leader, window, cx| {
10548        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10549    });
10550    follower
10551        .update(cx, |follower, window, cx| {
10552            follower.apply_update_proto(
10553                &project,
10554                pending_update.borrow_mut().take().unwrap(),
10555                window,
10556                cx,
10557            )
10558        })
10559        .unwrap()
10560        .await
10561        .unwrap();
10562    _ = follower.update(cx, |follower, _, cx| {
10563        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10564    });
10565    assert!(*is_still_following.borrow());
10566    assert_eq!(*follower_edit_event_count.borrow(), 0);
10567
10568    // Update the scroll position only
10569    _ = leader.update(cx, |leader, window, cx| {
10570        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10571    });
10572    follower
10573        .update(cx, |follower, window, cx| {
10574            follower.apply_update_proto(
10575                &project,
10576                pending_update.borrow_mut().take().unwrap(),
10577                window,
10578                cx,
10579            )
10580        })
10581        .unwrap()
10582        .await
10583        .unwrap();
10584    assert_eq!(
10585        follower
10586            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10587            .unwrap(),
10588        gpui::Point::new(1.5, 3.5)
10589    );
10590    assert!(*is_still_following.borrow());
10591    assert_eq!(*follower_edit_event_count.borrow(), 0);
10592
10593    // Update the selections and scroll position. The follower's scroll position is updated
10594    // via autoscroll, not via the leader's exact scroll position.
10595    _ = leader.update(cx, |leader, window, cx| {
10596        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10597        leader.request_autoscroll(Autoscroll::newest(), cx);
10598        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10599    });
10600    follower
10601        .update(cx, |follower, window, cx| {
10602            follower.apply_update_proto(
10603                &project,
10604                pending_update.borrow_mut().take().unwrap(),
10605                window,
10606                cx,
10607            )
10608        })
10609        .unwrap()
10610        .await
10611        .unwrap();
10612    _ = follower.update(cx, |follower, _, cx| {
10613        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10614        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10615    });
10616    assert!(*is_still_following.borrow());
10617
10618    // Creating a pending selection that precedes another selection
10619    _ = leader.update(cx, |leader, window, cx| {
10620        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10621        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10622    });
10623    follower
10624        .update(cx, |follower, window, cx| {
10625            follower.apply_update_proto(
10626                &project,
10627                pending_update.borrow_mut().take().unwrap(),
10628                window,
10629                cx,
10630            )
10631        })
10632        .unwrap()
10633        .await
10634        .unwrap();
10635    _ = follower.update(cx, |follower, _, cx| {
10636        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10637    });
10638    assert!(*is_still_following.borrow());
10639
10640    // Extend the pending selection so that it surrounds another selection
10641    _ = leader.update(cx, |leader, window, cx| {
10642        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10643    });
10644    follower
10645        .update(cx, |follower, window, cx| {
10646            follower.apply_update_proto(
10647                &project,
10648                pending_update.borrow_mut().take().unwrap(),
10649                window,
10650                cx,
10651            )
10652        })
10653        .unwrap()
10654        .await
10655        .unwrap();
10656    _ = follower.update(cx, |follower, _, cx| {
10657        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10658    });
10659
10660    // Scrolling locally breaks the follow
10661    _ = follower.update(cx, |follower, window, cx| {
10662        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10663        follower.set_scroll_anchor(
10664            ScrollAnchor {
10665                anchor: top_anchor,
10666                offset: gpui::Point::new(0.0, 0.5),
10667            },
10668            window,
10669            cx,
10670        );
10671    });
10672    assert!(!(*is_still_following.borrow()));
10673}
10674
10675#[gpui::test]
10676async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10677    init_test(cx, |_| {});
10678
10679    let fs = FakeFs::new(cx.executor());
10680    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10681    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10682    let pane = workspace
10683        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10684        .unwrap();
10685
10686    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10687
10688    let leader = pane.update_in(cx, |_, window, cx| {
10689        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10690        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10691    });
10692
10693    // Start following the editor when it has no excerpts.
10694    let mut state_message =
10695        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10696    let workspace_entity = workspace.root(cx).unwrap();
10697    let follower_1 = cx
10698        .update_window(*workspace.deref(), |_, window, cx| {
10699            Editor::from_state_proto(
10700                workspace_entity,
10701                ViewId {
10702                    creator: Default::default(),
10703                    id: 0,
10704                },
10705                &mut state_message,
10706                window,
10707                cx,
10708            )
10709        })
10710        .unwrap()
10711        .unwrap()
10712        .await
10713        .unwrap();
10714
10715    let update_message = Rc::new(RefCell::new(None));
10716    follower_1.update_in(cx, {
10717        let update = update_message.clone();
10718        |_, window, cx| {
10719            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10720                leader.read(cx).add_event_to_update_proto(
10721                    event,
10722                    &mut update.borrow_mut(),
10723                    window,
10724                    cx,
10725                );
10726            })
10727            .detach();
10728        }
10729    });
10730
10731    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10732        (
10733            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10734            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10735        )
10736    });
10737
10738    // Insert some excerpts.
10739    leader.update(cx, |leader, cx| {
10740        leader.buffer.update(cx, |multibuffer, cx| {
10741            let excerpt_ids = multibuffer.push_excerpts(
10742                buffer_1.clone(),
10743                [
10744                    ExcerptRange {
10745                        context: 1..6,
10746                        primary: None,
10747                    },
10748                    ExcerptRange {
10749                        context: 12..15,
10750                        primary: None,
10751                    },
10752                    ExcerptRange {
10753                        context: 0..3,
10754                        primary: None,
10755                    },
10756                ],
10757                cx,
10758            );
10759            multibuffer.insert_excerpts_after(
10760                excerpt_ids[0],
10761                buffer_2.clone(),
10762                [
10763                    ExcerptRange {
10764                        context: 8..12,
10765                        primary: None,
10766                    },
10767                    ExcerptRange {
10768                        context: 0..6,
10769                        primary: None,
10770                    },
10771                ],
10772                cx,
10773            );
10774        });
10775    });
10776
10777    // Apply the update of adding the excerpts.
10778    follower_1
10779        .update_in(cx, |follower, window, cx| {
10780            follower.apply_update_proto(
10781                &project,
10782                update_message.borrow().clone().unwrap(),
10783                window,
10784                cx,
10785            )
10786        })
10787        .await
10788        .unwrap();
10789    assert_eq!(
10790        follower_1.update(cx, |editor, cx| editor.text(cx)),
10791        leader.update(cx, |editor, cx| editor.text(cx))
10792    );
10793    update_message.borrow_mut().take();
10794
10795    // Start following separately after it already has excerpts.
10796    let mut state_message =
10797        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10798    let workspace_entity = workspace.root(cx).unwrap();
10799    let follower_2 = cx
10800        .update_window(*workspace.deref(), |_, window, cx| {
10801            Editor::from_state_proto(
10802                workspace_entity,
10803                ViewId {
10804                    creator: Default::default(),
10805                    id: 0,
10806                },
10807                &mut state_message,
10808                window,
10809                cx,
10810            )
10811        })
10812        .unwrap()
10813        .unwrap()
10814        .await
10815        .unwrap();
10816    assert_eq!(
10817        follower_2.update(cx, |editor, cx| editor.text(cx)),
10818        leader.update(cx, |editor, cx| editor.text(cx))
10819    );
10820
10821    // Remove some excerpts.
10822    leader.update(cx, |leader, cx| {
10823        leader.buffer.update(cx, |multibuffer, cx| {
10824            let excerpt_ids = multibuffer.excerpt_ids();
10825            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10826            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10827        });
10828    });
10829
10830    // Apply the update of removing the excerpts.
10831    follower_1
10832        .update_in(cx, |follower, window, cx| {
10833            follower.apply_update_proto(
10834                &project,
10835                update_message.borrow().clone().unwrap(),
10836                window,
10837                cx,
10838            )
10839        })
10840        .await
10841        .unwrap();
10842    follower_2
10843        .update_in(cx, |follower, window, cx| {
10844            follower.apply_update_proto(
10845                &project,
10846                update_message.borrow().clone().unwrap(),
10847                window,
10848                cx,
10849            )
10850        })
10851        .await
10852        .unwrap();
10853    update_message.borrow_mut().take();
10854    assert_eq!(
10855        follower_1.update(cx, |editor, cx| editor.text(cx)),
10856        leader.update(cx, |editor, cx| editor.text(cx))
10857    );
10858}
10859
10860#[gpui::test]
10861async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
10862    init_test(cx, |_| {});
10863
10864    let mut cx = EditorTestContext::new(cx).await;
10865    let lsp_store =
10866        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10867
10868    cx.set_state(indoc! {"
10869        ˇfn func(abc def: i32) -> u32 {
10870        }
10871    "});
10872
10873    cx.update(|_, cx| {
10874        lsp_store.update(cx, |lsp_store, cx| {
10875            lsp_store
10876                .update_diagnostics(
10877                    LanguageServerId(0),
10878                    lsp::PublishDiagnosticsParams {
10879                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10880                        version: None,
10881                        diagnostics: vec![
10882                            lsp::Diagnostic {
10883                                range: lsp::Range::new(
10884                                    lsp::Position::new(0, 11),
10885                                    lsp::Position::new(0, 12),
10886                                ),
10887                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10888                                ..Default::default()
10889                            },
10890                            lsp::Diagnostic {
10891                                range: lsp::Range::new(
10892                                    lsp::Position::new(0, 12),
10893                                    lsp::Position::new(0, 15),
10894                                ),
10895                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10896                                ..Default::default()
10897                            },
10898                            lsp::Diagnostic {
10899                                range: lsp::Range::new(
10900                                    lsp::Position::new(0, 25),
10901                                    lsp::Position::new(0, 28),
10902                                ),
10903                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10904                                ..Default::default()
10905                            },
10906                        ],
10907                    },
10908                    &[],
10909                    cx,
10910                )
10911                .unwrap()
10912        });
10913    });
10914
10915    executor.run_until_parked();
10916
10917    cx.update_editor(|editor, window, cx| {
10918        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10919    });
10920
10921    cx.assert_editor_state(indoc! {"
10922        fn func(abc def: i32) -> ˇu32 {
10923        }
10924    "});
10925
10926    cx.update_editor(|editor, window, cx| {
10927        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10928    });
10929
10930    cx.assert_editor_state(indoc! {"
10931        fn func(abc ˇdef: i32) -> u32 {
10932        }
10933    "});
10934
10935    cx.update_editor(|editor, window, cx| {
10936        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10937    });
10938
10939    cx.assert_editor_state(indoc! {"
10940        fn func(abcˇ def: i32) -> u32 {
10941        }
10942    "});
10943
10944    cx.update_editor(|editor, window, cx| {
10945        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10946    });
10947
10948    cx.assert_editor_state(indoc! {"
10949        fn func(abc def: i32) -> ˇu32 {
10950        }
10951    "});
10952}
10953
10954#[gpui::test]
10955async fn cycle_through_same_place_diagnostics(
10956    executor: BackgroundExecutor,
10957    cx: &mut TestAppContext,
10958) {
10959    init_test(cx, |_| {});
10960
10961    let mut cx = EditorTestContext::new(cx).await;
10962    let lsp_store =
10963        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10964
10965    cx.set_state(indoc! {"
10966        ˇfn func(abc def: i32) -> u32 {
10967        }
10968    "});
10969
10970    cx.update(|_, cx| {
10971        lsp_store.update(cx, |lsp_store, cx| {
10972            lsp_store
10973                .update_diagnostics(
10974                    LanguageServerId(0),
10975                    lsp::PublishDiagnosticsParams {
10976                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10977                        version: None,
10978                        diagnostics: vec![
10979                            lsp::Diagnostic {
10980                                range: lsp::Range::new(
10981                                    lsp::Position::new(0, 11),
10982                                    lsp::Position::new(0, 12),
10983                                ),
10984                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10985                                ..Default::default()
10986                            },
10987                            lsp::Diagnostic {
10988                                range: lsp::Range::new(
10989                                    lsp::Position::new(0, 12),
10990                                    lsp::Position::new(0, 15),
10991                                ),
10992                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10993                                ..Default::default()
10994                            },
10995                            lsp::Diagnostic {
10996                                range: lsp::Range::new(
10997                                    lsp::Position::new(0, 12),
10998                                    lsp::Position::new(0, 15),
10999                                ),
11000                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11001                                ..Default::default()
11002                            },
11003                            lsp::Diagnostic {
11004                                range: lsp::Range::new(
11005                                    lsp::Position::new(0, 25),
11006                                    lsp::Position::new(0, 28),
11007                                ),
11008                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11009                                ..Default::default()
11010                            },
11011                        ],
11012                    },
11013                    &[],
11014                    cx,
11015                )
11016                .unwrap()
11017        });
11018    });
11019    executor.run_until_parked();
11020
11021    //// Backward
11022
11023    // Fourth diagnostic
11024    cx.update_editor(|editor, window, cx| {
11025        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11026    });
11027    cx.assert_editor_state(indoc! {"
11028        fn func(abc def: i32) -> ˇu32 {
11029        }
11030    "});
11031
11032    // Third diagnostic
11033    cx.update_editor(|editor, window, cx| {
11034        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11035    });
11036    cx.assert_editor_state(indoc! {"
11037        fn func(abc ˇdef: i32) -> u32 {
11038        }
11039    "});
11040
11041    // Second diagnostic, same place
11042    cx.update_editor(|editor, window, cx| {
11043        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11044    });
11045    cx.assert_editor_state(indoc! {"
11046        fn func(abc ˇdef: i32) -> u32 {
11047        }
11048    "});
11049
11050    // First diagnostic
11051    cx.update_editor(|editor, window, cx| {
11052        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11053    });
11054    cx.assert_editor_state(indoc! {"
11055        fn func(abcˇ def: i32) -> u32 {
11056        }
11057    "});
11058
11059    // Wrapped over, fourth diagnostic
11060    cx.update_editor(|editor, window, cx| {
11061        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
11062    });
11063    cx.assert_editor_state(indoc! {"
11064        fn func(abc def: i32) -> ˇu32 {
11065        }
11066    "});
11067
11068    cx.update_editor(|editor, window, cx| {
11069        editor.move_to_beginning(&MoveToBeginning, window, cx);
11070    });
11071    cx.assert_editor_state(indoc! {"
11072        ˇfn func(abc def: i32) -> u32 {
11073        }
11074    "});
11075
11076    //// Forward
11077
11078    // First diagnostic
11079    cx.update_editor(|editor, window, cx| {
11080        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11081    });
11082    cx.assert_editor_state(indoc! {"
11083        fn func(abcˇ def: i32) -> u32 {
11084        }
11085    "});
11086
11087    // Second diagnostic
11088    cx.update_editor(|editor, window, cx| {
11089        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11090    });
11091    cx.assert_editor_state(indoc! {"
11092        fn func(abc ˇdef: i32) -> u32 {
11093        }
11094    "});
11095
11096    // Third diagnostic, same place
11097    cx.update_editor(|editor, window, cx| {
11098        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11099    });
11100    cx.assert_editor_state(indoc! {"
11101        fn func(abc ˇdef: i32) -> u32 {
11102        }
11103    "});
11104
11105    // Fourth diagnostic
11106    cx.update_editor(|editor, window, cx| {
11107        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11108    });
11109    cx.assert_editor_state(indoc! {"
11110        fn func(abc def: i32) -> ˇu32 {
11111        }
11112    "});
11113
11114    // Wrapped around, first diagnostic
11115    cx.update_editor(|editor, window, cx| {
11116        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11117    });
11118    cx.assert_editor_state(indoc! {"
11119        fn func(abcˇ def: i32) -> u32 {
11120        }
11121    "});
11122}
11123
11124#[gpui::test]
11125async fn active_diagnostics_dismiss_after_invalidation(
11126    executor: BackgroundExecutor,
11127    cx: &mut TestAppContext,
11128) {
11129    init_test(cx, |_| {});
11130
11131    let mut cx = EditorTestContext::new(cx).await;
11132    let lsp_store =
11133        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11134
11135    cx.set_state(indoc! {"
11136        ˇfn func(abc def: i32) -> u32 {
11137        }
11138    "});
11139
11140    let message = "Something's wrong!";
11141    cx.update(|_, cx| {
11142        lsp_store.update(cx, |lsp_store, cx| {
11143            lsp_store
11144                .update_diagnostics(
11145                    LanguageServerId(0),
11146                    lsp::PublishDiagnosticsParams {
11147                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11148                        version: None,
11149                        diagnostics: vec![lsp::Diagnostic {
11150                            range: lsp::Range::new(
11151                                lsp::Position::new(0, 11),
11152                                lsp::Position::new(0, 12),
11153                            ),
11154                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11155                            message: message.to_string(),
11156                            ..Default::default()
11157                        }],
11158                    },
11159                    &[],
11160                    cx,
11161                )
11162                .unwrap()
11163        });
11164    });
11165    executor.run_until_parked();
11166
11167    cx.update_editor(|editor, window, cx| {
11168        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11169        assert_eq!(
11170            editor
11171                .active_diagnostics
11172                .as_ref()
11173                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11174            Some(message),
11175            "Should have a diagnostics group activated"
11176        );
11177    });
11178    cx.assert_editor_state(indoc! {"
11179        fn func(abcˇ def: i32) -> u32 {
11180        }
11181    "});
11182
11183    cx.update(|_, cx| {
11184        lsp_store.update(cx, |lsp_store, cx| {
11185            lsp_store
11186                .update_diagnostics(
11187                    LanguageServerId(0),
11188                    lsp::PublishDiagnosticsParams {
11189                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11190                        version: None,
11191                        diagnostics: Vec::new(),
11192                    },
11193                    &[],
11194                    cx,
11195                )
11196                .unwrap()
11197        });
11198    });
11199    executor.run_until_parked();
11200    cx.update_editor(|editor, _, _| {
11201        assert_eq!(
11202            editor.active_diagnostics, None,
11203            "After no diagnostics set to the editor, no diagnostics should be active"
11204        );
11205    });
11206    cx.assert_editor_state(indoc! {"
11207        fn func(abcˇ def: i32) -> u32 {
11208        }
11209    "});
11210
11211    cx.update_editor(|editor, window, cx| {
11212        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11213        assert_eq!(
11214            editor.active_diagnostics, None,
11215            "Should be no diagnostics to go to and activate"
11216        );
11217    });
11218    cx.assert_editor_state(indoc! {"
11219        fn func(abcˇ def: i32) -> u32 {
11220        }
11221    "});
11222}
11223
11224#[gpui::test]
11225async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11226    init_test(cx, |_| {});
11227
11228    let mut cx = EditorTestContext::new(cx).await;
11229
11230    cx.set_state(indoc! {"
11231        fn func(abˇc def: i32) -> u32 {
11232        }
11233    "});
11234    let lsp_store =
11235        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11236
11237    cx.update(|_, cx| {
11238        lsp_store.update(cx, |lsp_store, cx| {
11239            lsp_store.update_diagnostics(
11240                LanguageServerId(0),
11241                lsp::PublishDiagnosticsParams {
11242                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11243                    version: None,
11244                    diagnostics: vec![lsp::Diagnostic {
11245                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11246                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11247                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11248                        ..Default::default()
11249                    }],
11250                },
11251                &[],
11252                cx,
11253            )
11254        })
11255    }).unwrap();
11256    cx.run_until_parked();
11257    cx.update_editor(|editor, window, cx| {
11258        hover_popover::hover(editor, &Default::default(), window, cx)
11259    });
11260    cx.run_until_parked();
11261    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11262}
11263
11264#[gpui::test]
11265async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11266    init_test(cx, |_| {});
11267
11268    let mut cx = EditorTestContext::new(cx).await;
11269
11270    let diff_base = r#"
11271        use some::mod;
11272
11273        const A: u32 = 42;
11274
11275        fn main() {
11276            println!("hello");
11277
11278            println!("world");
11279        }
11280        "#
11281    .unindent();
11282
11283    // Edits are modified, removed, modified, added
11284    cx.set_state(
11285        &r#"
11286        use some::modified;
11287
11288        ˇ
11289        fn main() {
11290            println!("hello there");
11291
11292            println!("around the");
11293            println!("world");
11294        }
11295        "#
11296        .unindent(),
11297    );
11298
11299    cx.set_head_text(&diff_base);
11300    executor.run_until_parked();
11301
11302    cx.update_editor(|editor, window, cx| {
11303        //Wrap around the bottom of the buffer
11304        for _ in 0..3 {
11305            editor.go_to_next_hunk(&GoToHunk, window, cx);
11306        }
11307    });
11308
11309    cx.assert_editor_state(
11310        &r#"
11311        ˇuse some::modified;
11312
11313
11314        fn main() {
11315            println!("hello there");
11316
11317            println!("around the");
11318            println!("world");
11319        }
11320        "#
11321        .unindent(),
11322    );
11323
11324    cx.update_editor(|editor, window, cx| {
11325        //Wrap around the top of the buffer
11326        for _ in 0..2 {
11327            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11328        }
11329    });
11330
11331    cx.assert_editor_state(
11332        &r#"
11333        use some::modified;
11334
11335
11336        fn main() {
11337        ˇ    println!("hello there");
11338
11339            println!("around the");
11340            println!("world");
11341        }
11342        "#
11343        .unindent(),
11344    );
11345
11346    cx.update_editor(|editor, window, cx| {
11347        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11348    });
11349
11350    cx.assert_editor_state(
11351        &r#"
11352        use some::modified;
11353
11354        ˇ
11355        fn main() {
11356            println!("hello there");
11357
11358            println!("around the");
11359            println!("world");
11360        }
11361        "#
11362        .unindent(),
11363    );
11364
11365    cx.update_editor(|editor, window, cx| {
11366        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11367    });
11368
11369    cx.assert_editor_state(
11370        &r#"
11371        ˇuse some::modified;
11372
11373
11374        fn main() {
11375            println!("hello there");
11376
11377            println!("around the");
11378            println!("world");
11379        }
11380        "#
11381        .unindent(),
11382    );
11383
11384    cx.update_editor(|editor, window, cx| {
11385        for _ in 0..2 {
11386            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11387        }
11388    });
11389
11390    cx.assert_editor_state(
11391        &r#"
11392        use some::modified;
11393
11394
11395        fn main() {
11396        ˇ    println!("hello there");
11397
11398            println!("around the");
11399            println!("world");
11400        }
11401        "#
11402        .unindent(),
11403    );
11404
11405    cx.update_editor(|editor, window, cx| {
11406        editor.fold(&Fold, window, cx);
11407    });
11408
11409    cx.update_editor(|editor, window, cx| {
11410        editor.go_to_next_hunk(&GoToHunk, window, cx);
11411    });
11412
11413    cx.assert_editor_state(
11414        &r#"
11415        ˇuse some::modified;
11416
11417
11418        fn main() {
11419            println!("hello there");
11420
11421            println!("around the");
11422            println!("world");
11423        }
11424        "#
11425        .unindent(),
11426    );
11427}
11428
11429#[test]
11430fn test_split_words() {
11431    fn split(text: &str) -> Vec<&str> {
11432        split_words(text).collect()
11433    }
11434
11435    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11436    assert_eq!(split("hello_world"), &["hello_", "world"]);
11437    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11438    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11439    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11440    assert_eq!(split("helloworld"), &["helloworld"]);
11441
11442    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11443}
11444
11445#[gpui::test]
11446async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11447    init_test(cx, |_| {});
11448
11449    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11450    let mut assert = |before, after| {
11451        let _state_context = cx.set_state(before);
11452        cx.run_until_parked();
11453        cx.update_editor(|editor, window, cx| {
11454            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11455        });
11456        cx.assert_editor_state(after);
11457    };
11458
11459    // Outside bracket jumps to outside of matching bracket
11460    assert("console.logˇ(var);", "console.log(var)ˇ;");
11461    assert("console.log(var)ˇ;", "console.logˇ(var);");
11462
11463    // Inside bracket jumps to inside of matching bracket
11464    assert("console.log(ˇvar);", "console.log(varˇ);");
11465    assert("console.log(varˇ);", "console.log(ˇvar);");
11466
11467    // When outside a bracket and inside, favor jumping to the inside bracket
11468    assert(
11469        "console.log('foo', [1, 2, 3]ˇ);",
11470        "console.log(ˇ'foo', [1, 2, 3]);",
11471    );
11472    assert(
11473        "console.log(ˇ'foo', [1, 2, 3]);",
11474        "console.log('foo', [1, 2, 3]ˇ);",
11475    );
11476
11477    // Bias forward if two options are equally likely
11478    assert(
11479        "let result = curried_fun()ˇ();",
11480        "let result = curried_fun()()ˇ;",
11481    );
11482
11483    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11484    assert(
11485        indoc! {"
11486            function test() {
11487                console.log('test')ˇ
11488            }"},
11489        indoc! {"
11490            function test() {
11491                console.logˇ('test')
11492            }"},
11493    );
11494}
11495
11496#[gpui::test]
11497async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11498    init_test(cx, |_| {});
11499
11500    let fs = FakeFs::new(cx.executor());
11501    fs.insert_tree(
11502        path!("/a"),
11503        json!({
11504            "main.rs": "fn main() { let a = 5; }",
11505            "other.rs": "// Test file",
11506        }),
11507    )
11508    .await;
11509    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11510
11511    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11512    language_registry.add(Arc::new(Language::new(
11513        LanguageConfig {
11514            name: "Rust".into(),
11515            matcher: LanguageMatcher {
11516                path_suffixes: vec!["rs".to_string()],
11517                ..Default::default()
11518            },
11519            brackets: BracketPairConfig {
11520                pairs: vec![BracketPair {
11521                    start: "{".to_string(),
11522                    end: "}".to_string(),
11523                    close: true,
11524                    surround: true,
11525                    newline: true,
11526                }],
11527                disabled_scopes_by_bracket_ix: Vec::new(),
11528            },
11529            ..Default::default()
11530        },
11531        Some(tree_sitter_rust::LANGUAGE.into()),
11532    )));
11533    let mut fake_servers = language_registry.register_fake_lsp(
11534        "Rust",
11535        FakeLspAdapter {
11536            capabilities: lsp::ServerCapabilities {
11537                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11538                    first_trigger_character: "{".to_string(),
11539                    more_trigger_character: None,
11540                }),
11541                ..Default::default()
11542            },
11543            ..Default::default()
11544        },
11545    );
11546
11547    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11548
11549    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11550
11551    let worktree_id = workspace
11552        .update(cx, |workspace, _, cx| {
11553            workspace.project().update(cx, |project, cx| {
11554                project.worktrees(cx).next().unwrap().read(cx).id()
11555            })
11556        })
11557        .unwrap();
11558
11559    let buffer = project
11560        .update(cx, |project, cx| {
11561            project.open_local_buffer(path!("/a/main.rs"), cx)
11562        })
11563        .await
11564        .unwrap();
11565    let editor_handle = workspace
11566        .update(cx, |workspace, window, cx| {
11567            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11568        })
11569        .unwrap()
11570        .await
11571        .unwrap()
11572        .downcast::<Editor>()
11573        .unwrap();
11574
11575    cx.executor().start_waiting();
11576    let fake_server = fake_servers.next().await.unwrap();
11577
11578    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11579        assert_eq!(
11580            params.text_document_position.text_document.uri,
11581            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11582        );
11583        assert_eq!(
11584            params.text_document_position.position,
11585            lsp::Position::new(0, 21),
11586        );
11587
11588        Ok(Some(vec![lsp::TextEdit {
11589            new_text: "]".to_string(),
11590            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11591        }]))
11592    });
11593
11594    editor_handle.update_in(cx, |editor, window, cx| {
11595        window.focus(&editor.focus_handle(cx));
11596        editor.change_selections(None, window, cx, |s| {
11597            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11598        });
11599        editor.handle_input("{", window, cx);
11600    });
11601
11602    cx.executor().run_until_parked();
11603
11604    buffer.update(cx, |buffer, _| {
11605        assert_eq!(
11606            buffer.text(),
11607            "fn main() { let a = {5}; }",
11608            "No extra braces from on type formatting should appear in the buffer"
11609        )
11610    });
11611}
11612
11613#[gpui::test]
11614async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11615    init_test(cx, |_| {});
11616
11617    let fs = FakeFs::new(cx.executor());
11618    fs.insert_tree(
11619        path!("/a"),
11620        json!({
11621            "main.rs": "fn main() { let a = 5; }",
11622            "other.rs": "// Test file",
11623        }),
11624    )
11625    .await;
11626
11627    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11628
11629    let server_restarts = Arc::new(AtomicUsize::new(0));
11630    let closure_restarts = Arc::clone(&server_restarts);
11631    let language_server_name = "test language server";
11632    let language_name: LanguageName = "Rust".into();
11633
11634    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11635    language_registry.add(Arc::new(Language::new(
11636        LanguageConfig {
11637            name: language_name.clone(),
11638            matcher: LanguageMatcher {
11639                path_suffixes: vec!["rs".to_string()],
11640                ..Default::default()
11641            },
11642            ..Default::default()
11643        },
11644        Some(tree_sitter_rust::LANGUAGE.into()),
11645    )));
11646    let mut fake_servers = language_registry.register_fake_lsp(
11647        "Rust",
11648        FakeLspAdapter {
11649            name: language_server_name,
11650            initialization_options: Some(json!({
11651                "testOptionValue": true
11652            })),
11653            initializer: Some(Box::new(move |fake_server| {
11654                let task_restarts = Arc::clone(&closure_restarts);
11655                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11656                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11657                    futures::future::ready(Ok(()))
11658                });
11659            })),
11660            ..Default::default()
11661        },
11662    );
11663
11664    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11665    let _buffer = project
11666        .update(cx, |project, cx| {
11667            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11668        })
11669        .await
11670        .unwrap();
11671    let _fake_server = fake_servers.next().await.unwrap();
11672    update_test_language_settings(cx, |language_settings| {
11673        language_settings.languages.insert(
11674            language_name.clone(),
11675            LanguageSettingsContent {
11676                tab_size: NonZeroU32::new(8),
11677                ..Default::default()
11678            },
11679        );
11680    });
11681    cx.executor().run_until_parked();
11682    assert_eq!(
11683        server_restarts.load(atomic::Ordering::Acquire),
11684        0,
11685        "Should not restart LSP server on an unrelated change"
11686    );
11687
11688    update_test_project_settings(cx, |project_settings| {
11689        project_settings.lsp.insert(
11690            "Some other server name".into(),
11691            LspSettings {
11692                binary: None,
11693                settings: None,
11694                initialization_options: Some(json!({
11695                    "some other init value": false
11696                })),
11697            },
11698        );
11699    });
11700    cx.executor().run_until_parked();
11701    assert_eq!(
11702        server_restarts.load(atomic::Ordering::Acquire),
11703        0,
11704        "Should not restart LSP server on an unrelated LSP settings change"
11705    );
11706
11707    update_test_project_settings(cx, |project_settings| {
11708        project_settings.lsp.insert(
11709            language_server_name.into(),
11710            LspSettings {
11711                binary: None,
11712                settings: None,
11713                initialization_options: Some(json!({
11714                    "anotherInitValue": false
11715                })),
11716            },
11717        );
11718    });
11719    cx.executor().run_until_parked();
11720    assert_eq!(
11721        server_restarts.load(atomic::Ordering::Acquire),
11722        1,
11723        "Should restart LSP server on a related LSP settings change"
11724    );
11725
11726    update_test_project_settings(cx, |project_settings| {
11727        project_settings.lsp.insert(
11728            language_server_name.into(),
11729            LspSettings {
11730                binary: None,
11731                settings: None,
11732                initialization_options: Some(json!({
11733                    "anotherInitValue": false
11734                })),
11735            },
11736        );
11737    });
11738    cx.executor().run_until_parked();
11739    assert_eq!(
11740        server_restarts.load(atomic::Ordering::Acquire),
11741        1,
11742        "Should not restart LSP server on a related LSP settings change that is the same"
11743    );
11744
11745    update_test_project_settings(cx, |project_settings| {
11746        project_settings.lsp.insert(
11747            language_server_name.into(),
11748            LspSettings {
11749                binary: None,
11750                settings: None,
11751                initialization_options: None,
11752            },
11753        );
11754    });
11755    cx.executor().run_until_parked();
11756    assert_eq!(
11757        server_restarts.load(atomic::Ordering::Acquire),
11758        2,
11759        "Should restart LSP server on another related LSP settings change"
11760    );
11761}
11762
11763#[gpui::test]
11764async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11765    init_test(cx, |_| {});
11766
11767    let mut cx = EditorLspTestContext::new_rust(
11768        lsp::ServerCapabilities {
11769            completion_provider: Some(lsp::CompletionOptions {
11770                trigger_characters: Some(vec![".".to_string()]),
11771                resolve_provider: Some(true),
11772                ..Default::default()
11773            }),
11774            ..Default::default()
11775        },
11776        cx,
11777    )
11778    .await;
11779
11780    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11781    cx.simulate_keystroke(".");
11782    let completion_item = lsp::CompletionItem {
11783        label: "some".into(),
11784        kind: Some(lsp::CompletionItemKind::SNIPPET),
11785        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11786        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11787            kind: lsp::MarkupKind::Markdown,
11788            value: "```rust\nSome(2)\n```".to_string(),
11789        })),
11790        deprecated: Some(false),
11791        sort_text: Some("fffffff2".to_string()),
11792        filter_text: Some("some".to_string()),
11793        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11794        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11795            range: lsp::Range {
11796                start: lsp::Position {
11797                    line: 0,
11798                    character: 22,
11799                },
11800                end: lsp::Position {
11801                    line: 0,
11802                    character: 22,
11803                },
11804            },
11805            new_text: "Some(2)".to_string(),
11806        })),
11807        additional_text_edits: Some(vec![lsp::TextEdit {
11808            range: lsp::Range {
11809                start: lsp::Position {
11810                    line: 0,
11811                    character: 20,
11812                },
11813                end: lsp::Position {
11814                    line: 0,
11815                    character: 22,
11816                },
11817            },
11818            new_text: "".to_string(),
11819        }]),
11820        ..Default::default()
11821    };
11822
11823    let closure_completion_item = completion_item.clone();
11824    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11825        let task_completion_item = closure_completion_item.clone();
11826        async move {
11827            Ok(Some(lsp::CompletionResponse::Array(vec![
11828                task_completion_item,
11829            ])))
11830        }
11831    });
11832
11833    request.next().await;
11834
11835    cx.condition(|editor, _| editor.context_menu_visible())
11836        .await;
11837    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11838        editor
11839            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11840            .unwrap()
11841    });
11842    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11843
11844    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11845        let task_completion_item = completion_item.clone();
11846        async move { Ok(task_completion_item) }
11847    })
11848    .next()
11849    .await
11850    .unwrap();
11851    apply_additional_edits.await.unwrap();
11852    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11853}
11854
11855#[gpui::test]
11856async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11857    init_test(cx, |_| {});
11858
11859    let mut cx = EditorLspTestContext::new_rust(
11860        lsp::ServerCapabilities {
11861            completion_provider: Some(lsp::CompletionOptions {
11862                trigger_characters: Some(vec![".".to_string()]),
11863                resolve_provider: Some(true),
11864                ..Default::default()
11865            }),
11866            ..Default::default()
11867        },
11868        cx,
11869    )
11870    .await;
11871
11872    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11873    cx.simulate_keystroke(".");
11874
11875    let item1 = lsp::CompletionItem {
11876        label: "method id()".to_string(),
11877        filter_text: Some("id".to_string()),
11878        detail: None,
11879        documentation: None,
11880        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11881            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11882            new_text: ".id".to_string(),
11883        })),
11884        ..lsp::CompletionItem::default()
11885    };
11886
11887    let item2 = lsp::CompletionItem {
11888        label: "other".to_string(),
11889        filter_text: Some("other".to_string()),
11890        detail: None,
11891        documentation: None,
11892        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11893            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11894            new_text: ".other".to_string(),
11895        })),
11896        ..lsp::CompletionItem::default()
11897    };
11898
11899    let item1 = item1.clone();
11900    cx.handle_request::<lsp::request::Completion, _, _>({
11901        let item1 = item1.clone();
11902        move |_, _, _| {
11903            let item1 = item1.clone();
11904            let item2 = item2.clone();
11905            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11906        }
11907    })
11908    .next()
11909    .await;
11910
11911    cx.condition(|editor, _| editor.context_menu_visible())
11912        .await;
11913    cx.update_editor(|editor, _, _| {
11914        let context_menu = editor.context_menu.borrow_mut();
11915        let context_menu = context_menu
11916            .as_ref()
11917            .expect("Should have the context menu deployed");
11918        match context_menu {
11919            CodeContextMenu::Completions(completions_menu) => {
11920                let completions = completions_menu.completions.borrow_mut();
11921                assert_eq!(
11922                    completions
11923                        .iter()
11924                        .map(|completion| &completion.label.text)
11925                        .collect::<Vec<_>>(),
11926                    vec!["method id()", "other"]
11927                )
11928            }
11929            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11930        }
11931    });
11932
11933    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11934        let item1 = item1.clone();
11935        move |_, item_to_resolve, _| {
11936            let item1 = item1.clone();
11937            async move {
11938                if item1 == item_to_resolve {
11939                    Ok(lsp::CompletionItem {
11940                        label: "method id()".to_string(),
11941                        filter_text: Some("id".to_string()),
11942                        detail: Some("Now resolved!".to_string()),
11943                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11944                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11945                            range: lsp::Range::new(
11946                                lsp::Position::new(0, 22),
11947                                lsp::Position::new(0, 22),
11948                            ),
11949                            new_text: ".id".to_string(),
11950                        })),
11951                        ..lsp::CompletionItem::default()
11952                    })
11953                } else {
11954                    Ok(item_to_resolve)
11955                }
11956            }
11957        }
11958    })
11959    .next()
11960    .await
11961    .unwrap();
11962    cx.run_until_parked();
11963
11964    cx.update_editor(|editor, window, cx| {
11965        editor.context_menu_next(&Default::default(), window, cx);
11966    });
11967
11968    cx.update_editor(|editor, _, _| {
11969        let context_menu = editor.context_menu.borrow_mut();
11970        let context_menu = context_menu
11971            .as_ref()
11972            .expect("Should have the context menu deployed");
11973        match context_menu {
11974            CodeContextMenu::Completions(completions_menu) => {
11975                let completions = completions_menu.completions.borrow_mut();
11976                assert_eq!(
11977                    completions
11978                        .iter()
11979                        .map(|completion| &completion.label.text)
11980                        .collect::<Vec<_>>(),
11981                    vec!["method id() Now resolved!", "other"],
11982                    "Should update first completion label, but not second as the filter text did not match."
11983                );
11984            }
11985            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11986        }
11987    });
11988}
11989
11990#[gpui::test]
11991async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
11992    init_test(cx, |_| {});
11993
11994    let mut cx = EditorLspTestContext::new_rust(
11995        lsp::ServerCapabilities {
11996            completion_provider: Some(lsp::CompletionOptions {
11997                trigger_characters: Some(vec![".".to_string()]),
11998                resolve_provider: Some(true),
11999                ..Default::default()
12000            }),
12001            ..Default::default()
12002        },
12003        cx,
12004    )
12005    .await;
12006
12007    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12008    cx.simulate_keystroke(".");
12009
12010    let unresolved_item_1 = lsp::CompletionItem {
12011        label: "id".to_string(),
12012        filter_text: Some("id".to_string()),
12013        detail: None,
12014        documentation: None,
12015        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12016            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12017            new_text: ".id".to_string(),
12018        })),
12019        ..lsp::CompletionItem::default()
12020    };
12021    let resolved_item_1 = lsp::CompletionItem {
12022        additional_text_edits: Some(vec![lsp::TextEdit {
12023            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12024            new_text: "!!".to_string(),
12025        }]),
12026        ..unresolved_item_1.clone()
12027    };
12028    let unresolved_item_2 = lsp::CompletionItem {
12029        label: "other".to_string(),
12030        filter_text: Some("other".to_string()),
12031        detail: None,
12032        documentation: None,
12033        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12034            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12035            new_text: ".other".to_string(),
12036        })),
12037        ..lsp::CompletionItem::default()
12038    };
12039    let resolved_item_2 = lsp::CompletionItem {
12040        additional_text_edits: Some(vec![lsp::TextEdit {
12041            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12042            new_text: "??".to_string(),
12043        }]),
12044        ..unresolved_item_2.clone()
12045    };
12046
12047    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12048    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12049    cx.lsp
12050        .server
12051        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12052            let unresolved_item_1 = unresolved_item_1.clone();
12053            let resolved_item_1 = resolved_item_1.clone();
12054            let unresolved_item_2 = unresolved_item_2.clone();
12055            let resolved_item_2 = resolved_item_2.clone();
12056            let resolve_requests_1 = resolve_requests_1.clone();
12057            let resolve_requests_2 = resolve_requests_2.clone();
12058            move |unresolved_request, _| {
12059                let unresolved_item_1 = unresolved_item_1.clone();
12060                let resolved_item_1 = resolved_item_1.clone();
12061                let unresolved_item_2 = unresolved_item_2.clone();
12062                let resolved_item_2 = resolved_item_2.clone();
12063                let resolve_requests_1 = resolve_requests_1.clone();
12064                let resolve_requests_2 = resolve_requests_2.clone();
12065                async move {
12066                    if unresolved_request == unresolved_item_1 {
12067                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12068                        Ok(resolved_item_1.clone())
12069                    } else if unresolved_request == unresolved_item_2 {
12070                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12071                        Ok(resolved_item_2.clone())
12072                    } else {
12073                        panic!("Unexpected completion item {unresolved_request:?}")
12074                    }
12075                }
12076            }
12077        })
12078        .detach();
12079
12080    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12081        let unresolved_item_1 = unresolved_item_1.clone();
12082        let unresolved_item_2 = unresolved_item_2.clone();
12083        async move {
12084            Ok(Some(lsp::CompletionResponse::Array(vec![
12085                unresolved_item_1,
12086                unresolved_item_2,
12087            ])))
12088        }
12089    })
12090    .next()
12091    .await;
12092
12093    cx.condition(|editor, _| editor.context_menu_visible())
12094        .await;
12095    cx.update_editor(|editor, _, _| {
12096        let context_menu = editor.context_menu.borrow_mut();
12097        let context_menu = context_menu
12098            .as_ref()
12099            .expect("Should have the context menu deployed");
12100        match context_menu {
12101            CodeContextMenu::Completions(completions_menu) => {
12102                let completions = completions_menu.completions.borrow_mut();
12103                assert_eq!(
12104                    completions
12105                        .iter()
12106                        .map(|completion| &completion.label.text)
12107                        .collect::<Vec<_>>(),
12108                    vec!["id", "other"]
12109                )
12110            }
12111            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12112        }
12113    });
12114    cx.run_until_parked();
12115
12116    cx.update_editor(|editor, window, cx| {
12117        editor.context_menu_next(&ContextMenuNext, window, cx);
12118    });
12119    cx.run_until_parked();
12120    cx.update_editor(|editor, window, cx| {
12121        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12122    });
12123    cx.run_until_parked();
12124    cx.update_editor(|editor, window, cx| {
12125        editor.context_menu_next(&ContextMenuNext, window, cx);
12126    });
12127    cx.run_until_parked();
12128    cx.update_editor(|editor, window, cx| {
12129        editor
12130            .compose_completion(&ComposeCompletion::default(), window, cx)
12131            .expect("No task returned")
12132    })
12133    .await
12134    .expect("Completion failed");
12135    cx.run_until_parked();
12136
12137    cx.update_editor(|editor, _, cx| {
12138        assert_eq!(
12139            resolve_requests_1.load(atomic::Ordering::Acquire),
12140            1,
12141            "Should always resolve once despite multiple selections"
12142        );
12143        assert_eq!(
12144            resolve_requests_2.load(atomic::Ordering::Acquire),
12145            1,
12146            "Should always resolve once after multiple selections and applying the completion"
12147        );
12148        assert_eq!(
12149            editor.text(cx),
12150            "fn main() { let a = ??.other; }",
12151            "Should use resolved data when applying the completion"
12152        );
12153    });
12154}
12155
12156#[gpui::test]
12157async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12158    init_test(cx, |_| {});
12159
12160    let item_0 = lsp::CompletionItem {
12161        label: "abs".into(),
12162        insert_text: Some("abs".into()),
12163        data: Some(json!({ "very": "special"})),
12164        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12165        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12166            lsp::InsertReplaceEdit {
12167                new_text: "abs".to_string(),
12168                insert: lsp::Range::default(),
12169                replace: lsp::Range::default(),
12170            },
12171        )),
12172        ..lsp::CompletionItem::default()
12173    };
12174    let items = iter::once(item_0.clone())
12175        .chain((11..51).map(|i| lsp::CompletionItem {
12176            label: format!("item_{}", i),
12177            insert_text: Some(format!("item_{}", i)),
12178            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12179            ..lsp::CompletionItem::default()
12180        }))
12181        .collect::<Vec<_>>();
12182
12183    let default_commit_characters = vec!["?".to_string()];
12184    let default_data = json!({ "default": "data"});
12185    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12186    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12187    let default_edit_range = lsp::Range {
12188        start: lsp::Position {
12189            line: 0,
12190            character: 5,
12191        },
12192        end: lsp::Position {
12193            line: 0,
12194            character: 5,
12195        },
12196    };
12197
12198    let item_0_out = lsp::CompletionItem {
12199        commit_characters: Some(default_commit_characters.clone()),
12200        insert_text_format: Some(default_insert_text_format),
12201        ..item_0
12202    };
12203    let items_out = iter::once(item_0_out)
12204        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
12205            commit_characters: Some(default_commit_characters.clone()),
12206            data: Some(default_data.clone()),
12207            insert_text_mode: Some(default_insert_text_mode),
12208            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12209                range: default_edit_range,
12210                new_text: item.label.clone(),
12211            })),
12212            ..item.clone()
12213        }))
12214        .collect::<Vec<lsp::CompletionItem>>();
12215
12216    let mut cx = EditorLspTestContext::new_rust(
12217        lsp::ServerCapabilities {
12218            completion_provider: Some(lsp::CompletionOptions {
12219                trigger_characters: Some(vec![".".to_string()]),
12220                resolve_provider: Some(true),
12221                ..Default::default()
12222            }),
12223            ..Default::default()
12224        },
12225        cx,
12226    )
12227    .await;
12228
12229    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12230    cx.simulate_keystroke(".");
12231
12232    let completion_data = default_data.clone();
12233    let completion_characters = default_commit_characters.clone();
12234    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12235        let default_data = completion_data.clone();
12236        let default_commit_characters = completion_characters.clone();
12237        let items = items.clone();
12238        async move {
12239            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12240                items,
12241                item_defaults: Some(lsp::CompletionListItemDefaults {
12242                    data: Some(default_data.clone()),
12243                    commit_characters: Some(default_commit_characters.clone()),
12244                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12245                        default_edit_range,
12246                    )),
12247                    insert_text_format: Some(default_insert_text_format),
12248                    insert_text_mode: Some(default_insert_text_mode),
12249                }),
12250                ..lsp::CompletionList::default()
12251            })))
12252        }
12253    })
12254    .next()
12255    .await;
12256
12257    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12258    cx.lsp
12259        .server
12260        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12261            let closure_resolved_items = resolved_items.clone();
12262            move |item_to_resolve, _| {
12263                let closure_resolved_items = closure_resolved_items.clone();
12264                async move {
12265                    closure_resolved_items.lock().push(item_to_resolve.clone());
12266                    Ok(item_to_resolve)
12267                }
12268            }
12269        })
12270        .detach();
12271
12272    cx.condition(|editor, _| editor.context_menu_visible())
12273        .await;
12274    cx.run_until_parked();
12275    cx.update_editor(|editor, _, _| {
12276        let menu = editor.context_menu.borrow_mut();
12277        match menu.as_ref().expect("should have the completions menu") {
12278            CodeContextMenu::Completions(completions_menu) => {
12279                assert_eq!(
12280                    completions_menu
12281                        .entries
12282                        .borrow()
12283                        .iter()
12284                        .map(|mat| mat.string.clone())
12285                        .collect::<Vec<String>>(),
12286                    items_out
12287                        .iter()
12288                        .map(|completion| completion.label.clone())
12289                        .collect::<Vec<String>>()
12290                );
12291            }
12292            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12293        }
12294    });
12295    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12296    // with 4 from the end.
12297    assert_eq!(
12298        *resolved_items.lock(),
12299        [
12300            &items_out[0..16],
12301            &items_out[items_out.len() - 4..items_out.len()]
12302        ]
12303        .concat()
12304        .iter()
12305        .cloned()
12306        .collect::<Vec<lsp::CompletionItem>>()
12307    );
12308    resolved_items.lock().clear();
12309
12310    cx.update_editor(|editor, window, cx| {
12311        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12312    });
12313    cx.run_until_parked();
12314    // Completions that have already been resolved are skipped.
12315    assert_eq!(
12316        *resolved_items.lock(),
12317        items_out[items_out.len() - 16..items_out.len() - 4]
12318            .iter()
12319            .cloned()
12320            .collect::<Vec<lsp::CompletionItem>>()
12321    );
12322    resolved_items.lock().clear();
12323}
12324
12325#[gpui::test]
12326async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12327    init_test(cx, |_| {});
12328
12329    let mut cx = EditorLspTestContext::new(
12330        Language::new(
12331            LanguageConfig {
12332                matcher: LanguageMatcher {
12333                    path_suffixes: vec!["jsx".into()],
12334                    ..Default::default()
12335                },
12336                overrides: [(
12337                    "element".into(),
12338                    LanguageConfigOverride {
12339                        word_characters: Override::Set(['-'].into_iter().collect()),
12340                        ..Default::default()
12341                    },
12342                )]
12343                .into_iter()
12344                .collect(),
12345                ..Default::default()
12346            },
12347            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12348        )
12349        .with_override_query("(jsx_self_closing_element) @element")
12350        .unwrap(),
12351        lsp::ServerCapabilities {
12352            completion_provider: Some(lsp::CompletionOptions {
12353                trigger_characters: Some(vec![":".to_string()]),
12354                ..Default::default()
12355            }),
12356            ..Default::default()
12357        },
12358        cx,
12359    )
12360    .await;
12361
12362    cx.lsp
12363        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12364            Ok(Some(lsp::CompletionResponse::Array(vec![
12365                lsp::CompletionItem {
12366                    label: "bg-blue".into(),
12367                    ..Default::default()
12368                },
12369                lsp::CompletionItem {
12370                    label: "bg-red".into(),
12371                    ..Default::default()
12372                },
12373                lsp::CompletionItem {
12374                    label: "bg-yellow".into(),
12375                    ..Default::default()
12376                },
12377            ])))
12378        });
12379
12380    cx.set_state(r#"<p class="bgˇ" />"#);
12381
12382    // Trigger completion when typing a dash, because the dash is an extra
12383    // word character in the 'element' scope, which contains the cursor.
12384    cx.simulate_keystroke("-");
12385    cx.executor().run_until_parked();
12386    cx.update_editor(|editor, _, _| {
12387        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12388        {
12389            assert_eq!(
12390                completion_menu_entries(&menu),
12391                &["bg-red", "bg-blue", "bg-yellow"]
12392            );
12393        } else {
12394            panic!("expected completion menu to be open");
12395        }
12396    });
12397
12398    cx.simulate_keystroke("l");
12399    cx.executor().run_until_parked();
12400    cx.update_editor(|editor, _, _| {
12401        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12402        {
12403            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12404        } else {
12405            panic!("expected completion menu to be open");
12406        }
12407    });
12408
12409    // When filtering completions, consider the character after the '-' to
12410    // be the start of a subword.
12411    cx.set_state(r#"<p class="yelˇ" />"#);
12412    cx.simulate_keystroke("l");
12413    cx.executor().run_until_parked();
12414    cx.update_editor(|editor, _, _| {
12415        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12416        {
12417            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12418        } else {
12419            panic!("expected completion menu to be open");
12420        }
12421    });
12422}
12423
12424fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12425    let entries = menu.entries.borrow();
12426    entries.iter().map(|mat| mat.string.clone()).collect()
12427}
12428
12429#[gpui::test]
12430async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12431    init_test(cx, |settings| {
12432        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12433            FormatterList(vec![Formatter::Prettier].into()),
12434        ))
12435    });
12436
12437    let fs = FakeFs::new(cx.executor());
12438    fs.insert_file(path!("/file.ts"), Default::default()).await;
12439
12440    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12441    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12442
12443    language_registry.add(Arc::new(Language::new(
12444        LanguageConfig {
12445            name: "TypeScript".into(),
12446            matcher: LanguageMatcher {
12447                path_suffixes: vec!["ts".to_string()],
12448                ..Default::default()
12449            },
12450            ..Default::default()
12451        },
12452        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12453    )));
12454    update_test_language_settings(cx, |settings| {
12455        settings.defaults.prettier = Some(PrettierSettings {
12456            allowed: true,
12457            ..PrettierSettings::default()
12458        });
12459    });
12460
12461    let test_plugin = "test_plugin";
12462    let _ = language_registry.register_fake_lsp(
12463        "TypeScript",
12464        FakeLspAdapter {
12465            prettier_plugins: vec![test_plugin],
12466            ..Default::default()
12467        },
12468    );
12469
12470    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12471    let buffer = project
12472        .update(cx, |project, cx| {
12473            project.open_local_buffer(path!("/file.ts"), cx)
12474        })
12475        .await
12476        .unwrap();
12477
12478    let buffer_text = "one\ntwo\nthree\n";
12479    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12480    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12481    editor.update_in(cx, |editor, window, cx| {
12482        editor.set_text(buffer_text, window, cx)
12483    });
12484
12485    editor
12486        .update_in(cx, |editor, window, cx| {
12487            editor.perform_format(
12488                project.clone(),
12489                FormatTrigger::Manual,
12490                FormatTarget::Buffers,
12491                window,
12492                cx,
12493            )
12494        })
12495        .unwrap()
12496        .await;
12497    assert_eq!(
12498        editor.update(cx, |editor, cx| editor.text(cx)),
12499        buffer_text.to_string() + prettier_format_suffix,
12500        "Test prettier formatting was not applied to the original buffer text",
12501    );
12502
12503    update_test_language_settings(cx, |settings| {
12504        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12505    });
12506    let format = editor.update_in(cx, |editor, window, cx| {
12507        editor.perform_format(
12508            project.clone(),
12509            FormatTrigger::Manual,
12510            FormatTarget::Buffers,
12511            window,
12512            cx,
12513        )
12514    });
12515    format.await.unwrap();
12516    assert_eq!(
12517        editor.update(cx, |editor, cx| editor.text(cx)),
12518        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12519        "Autoformatting (via test prettier) was not applied to the original buffer text",
12520    );
12521}
12522
12523#[gpui::test]
12524async fn test_addition_reverts(cx: &mut TestAppContext) {
12525    init_test(cx, |_| {});
12526    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12527    let base_text = indoc! {r#"
12528        struct Row;
12529        struct Row1;
12530        struct Row2;
12531
12532        struct Row4;
12533        struct Row5;
12534        struct Row6;
12535
12536        struct Row8;
12537        struct Row9;
12538        struct Row10;"#};
12539
12540    // When addition hunks are not adjacent to carets, no hunk revert is performed
12541    assert_hunk_revert(
12542        indoc! {r#"struct Row;
12543                   struct Row1;
12544                   struct Row1.1;
12545                   struct Row1.2;
12546                   struct Row2;ˇ
12547
12548                   struct Row4;
12549                   struct Row5;
12550                   struct Row6;
12551
12552                   struct Row8;
12553                   ˇstruct Row9;
12554                   struct Row9.1;
12555                   struct Row9.2;
12556                   struct Row9.3;
12557                   struct Row10;"#},
12558        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12559        indoc! {r#"struct Row;
12560                   struct Row1;
12561                   struct Row1.1;
12562                   struct Row1.2;
12563                   struct Row2;ˇ
12564
12565                   struct Row4;
12566                   struct Row5;
12567                   struct Row6;
12568
12569                   struct Row8;
12570                   ˇstruct Row9;
12571                   struct Row9.1;
12572                   struct Row9.2;
12573                   struct Row9.3;
12574                   struct Row10;"#},
12575        base_text,
12576        &mut cx,
12577    );
12578    // Same for selections
12579    assert_hunk_revert(
12580        indoc! {r#"struct Row;
12581                   struct Row1;
12582                   struct Row2;
12583                   struct Row2.1;
12584                   struct Row2.2;
12585                   «ˇ
12586                   struct Row4;
12587                   struct» Row5;
12588                   «struct Row6;
12589                   ˇ»
12590                   struct Row9.1;
12591                   struct Row9.2;
12592                   struct Row9.3;
12593                   struct Row8;
12594                   struct Row9;
12595                   struct Row10;"#},
12596        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12597        indoc! {r#"struct Row;
12598                   struct Row1;
12599                   struct Row2;
12600                   struct Row2.1;
12601                   struct Row2.2;
12602                   «ˇ
12603                   struct Row4;
12604                   struct» Row5;
12605                   «struct Row6;
12606                   ˇ»
12607                   struct Row9.1;
12608                   struct Row9.2;
12609                   struct Row9.3;
12610                   struct Row8;
12611                   struct Row9;
12612                   struct Row10;"#},
12613        base_text,
12614        &mut cx,
12615    );
12616
12617    // When carets and selections intersect the addition hunks, those are reverted.
12618    // Adjacent carets got merged.
12619    assert_hunk_revert(
12620        indoc! {r#"struct Row;
12621                   ˇ// something on the top
12622                   struct Row1;
12623                   struct Row2;
12624                   struct Roˇw3.1;
12625                   struct Row2.2;
12626                   struct Row2.3;ˇ
12627
12628                   struct Row4;
12629                   struct ˇRow5.1;
12630                   struct Row5.2;
12631                   struct «Rowˇ»5.3;
12632                   struct Row5;
12633                   struct Row6;
12634                   ˇ
12635                   struct Row9.1;
12636                   struct «Rowˇ»9.2;
12637                   struct «ˇRow»9.3;
12638                   struct Row8;
12639                   struct Row9;
12640                   «ˇ// something on bottom»
12641                   struct Row10;"#},
12642        vec![
12643            DiffHunkStatusKind::Added,
12644            DiffHunkStatusKind::Added,
12645            DiffHunkStatusKind::Added,
12646            DiffHunkStatusKind::Added,
12647            DiffHunkStatusKind::Added,
12648        ],
12649        indoc! {r#"struct Row;
12650                   ˇstruct Row1;
12651                   struct Row2;
12652                   ˇ
12653                   struct Row4;
12654                   ˇstruct Row5;
12655                   struct Row6;
12656                   ˇ
12657                   ˇstruct Row8;
12658                   struct Row9;
12659                   ˇstruct Row10;"#},
12660        base_text,
12661        &mut cx,
12662    );
12663}
12664
12665#[gpui::test]
12666async fn test_modification_reverts(cx: &mut TestAppContext) {
12667    init_test(cx, |_| {});
12668    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12669    let base_text = indoc! {r#"
12670        struct Row;
12671        struct Row1;
12672        struct Row2;
12673
12674        struct Row4;
12675        struct Row5;
12676        struct Row6;
12677
12678        struct Row8;
12679        struct Row9;
12680        struct Row10;"#};
12681
12682    // Modification hunks behave the same as the addition ones.
12683    assert_hunk_revert(
12684        indoc! {r#"struct Row;
12685                   struct Row1;
12686                   struct Row33;
12687                   ˇ
12688                   struct Row4;
12689                   struct Row5;
12690                   struct Row6;
12691                   ˇ
12692                   struct Row99;
12693                   struct Row9;
12694                   struct Row10;"#},
12695        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12696        indoc! {r#"struct Row;
12697                   struct Row1;
12698                   struct Row33;
12699                   ˇ
12700                   struct Row4;
12701                   struct Row5;
12702                   struct Row6;
12703                   ˇ
12704                   struct Row99;
12705                   struct Row9;
12706                   struct Row10;"#},
12707        base_text,
12708        &mut cx,
12709    );
12710    assert_hunk_revert(
12711        indoc! {r#"struct Row;
12712                   struct Row1;
12713                   struct Row33;
12714                   «ˇ
12715                   struct Row4;
12716                   struct» Row5;
12717                   «struct Row6;
12718                   ˇ»
12719                   struct Row99;
12720                   struct Row9;
12721                   struct Row10;"#},
12722        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
12723        indoc! {r#"struct Row;
12724                   struct Row1;
12725                   struct Row33;
12726                   «ˇ
12727                   struct Row4;
12728                   struct» Row5;
12729                   «struct Row6;
12730                   ˇ»
12731                   struct Row99;
12732                   struct Row9;
12733                   struct Row10;"#},
12734        base_text,
12735        &mut cx,
12736    );
12737
12738    assert_hunk_revert(
12739        indoc! {r#"ˇstruct Row1.1;
12740                   struct Row1;
12741                   «ˇstr»uct Row22;
12742
12743                   struct ˇRow44;
12744                   struct Row5;
12745                   struct «Rˇ»ow66;ˇ
12746
12747                   «struˇ»ct Row88;
12748                   struct Row9;
12749                   struct Row1011;ˇ"#},
12750        vec![
12751            DiffHunkStatusKind::Modified,
12752            DiffHunkStatusKind::Modified,
12753            DiffHunkStatusKind::Modified,
12754            DiffHunkStatusKind::Modified,
12755            DiffHunkStatusKind::Modified,
12756            DiffHunkStatusKind::Modified,
12757        ],
12758        indoc! {r#"struct Row;
12759                   ˇstruct Row1;
12760                   struct Row2;
12761                   ˇ
12762                   struct Row4;
12763                   ˇstruct Row5;
12764                   struct Row6;
12765                   ˇ
12766                   struct Row8;
12767                   ˇstruct Row9;
12768                   struct Row10;ˇ"#},
12769        base_text,
12770        &mut cx,
12771    );
12772}
12773
12774#[gpui::test]
12775async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12776    init_test(cx, |_| {});
12777    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12778    let base_text = indoc! {r#"
12779        one
12780
12781        two
12782        three
12783        "#};
12784
12785    cx.set_head_text(base_text);
12786    cx.set_state("\nˇ\n");
12787    cx.executor().run_until_parked();
12788    cx.update_editor(|editor, _window, cx| {
12789        editor.expand_selected_diff_hunks(cx);
12790    });
12791    cx.executor().run_until_parked();
12792    cx.update_editor(|editor, window, cx| {
12793        editor.backspace(&Default::default(), window, cx);
12794    });
12795    cx.run_until_parked();
12796    cx.assert_state_with_diff(
12797        indoc! {r#"
12798
12799        - two
12800        - threeˇ
12801        +
12802        "#}
12803        .to_string(),
12804    );
12805}
12806
12807#[gpui::test]
12808async fn test_deletion_reverts(cx: &mut TestAppContext) {
12809    init_test(cx, |_| {});
12810    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12811    let base_text = indoc! {r#"struct Row;
12812struct Row1;
12813struct Row2;
12814
12815struct Row4;
12816struct Row5;
12817struct Row6;
12818
12819struct Row8;
12820struct Row9;
12821struct Row10;"#};
12822
12823    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12824    assert_hunk_revert(
12825        indoc! {r#"struct Row;
12826                   struct Row2;
12827
12828                   ˇstruct Row4;
12829                   struct Row5;
12830                   struct Row6;
12831                   ˇ
12832                   struct Row8;
12833                   struct Row10;"#},
12834        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12835        indoc! {r#"struct Row;
12836                   struct Row2;
12837
12838                   ˇstruct Row4;
12839                   struct Row5;
12840                   struct Row6;
12841                   ˇ
12842                   struct Row8;
12843                   struct Row10;"#},
12844        base_text,
12845        &mut cx,
12846    );
12847    assert_hunk_revert(
12848        indoc! {r#"struct Row;
12849                   struct Row2;
12850
12851                   «ˇstruct Row4;
12852                   struct» Row5;
12853                   «struct Row6;
12854                   ˇ»
12855                   struct Row8;
12856                   struct Row10;"#},
12857        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12858        indoc! {r#"struct Row;
12859                   struct Row2;
12860
12861                   «ˇstruct Row4;
12862                   struct» Row5;
12863                   «struct Row6;
12864                   ˇ»
12865                   struct Row8;
12866                   struct Row10;"#},
12867        base_text,
12868        &mut cx,
12869    );
12870
12871    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12872    assert_hunk_revert(
12873        indoc! {r#"struct Row;
12874                   ˇstruct Row2;
12875
12876                   struct Row4;
12877                   struct Row5;
12878                   struct Row6;
12879
12880                   struct Row8;ˇ
12881                   struct Row10;"#},
12882        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
12883        indoc! {r#"struct Row;
12884                   struct Row1;
12885                   ˇstruct Row2;
12886
12887                   struct Row4;
12888                   struct Row5;
12889                   struct Row6;
12890
12891                   struct Row8;ˇ
12892                   struct Row9;
12893                   struct Row10;"#},
12894        base_text,
12895        &mut cx,
12896    );
12897    assert_hunk_revert(
12898        indoc! {r#"struct Row;
12899                   struct Row2«ˇ;
12900                   struct Row4;
12901                   struct» Row5;
12902                   «struct Row6;
12903
12904                   struct Row8;ˇ»
12905                   struct Row10;"#},
12906        vec![
12907            DiffHunkStatusKind::Deleted,
12908            DiffHunkStatusKind::Deleted,
12909            DiffHunkStatusKind::Deleted,
12910        ],
12911        indoc! {r#"struct Row;
12912                   struct Row1;
12913                   struct Row2«ˇ;
12914
12915                   struct Row4;
12916                   struct» Row5;
12917                   «struct Row6;
12918
12919                   struct Row8;ˇ»
12920                   struct Row9;
12921                   struct Row10;"#},
12922        base_text,
12923        &mut cx,
12924    );
12925}
12926
12927#[gpui::test]
12928async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
12929    init_test(cx, |_| {});
12930
12931    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12932    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12933    let base_text_3 =
12934        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12935
12936    let text_1 = edit_first_char_of_every_line(base_text_1);
12937    let text_2 = edit_first_char_of_every_line(base_text_2);
12938    let text_3 = edit_first_char_of_every_line(base_text_3);
12939
12940    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12941    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12942    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12943
12944    let multibuffer = cx.new(|cx| {
12945        let mut multibuffer = MultiBuffer::new(ReadWrite);
12946        multibuffer.push_excerpts(
12947            buffer_1.clone(),
12948            [
12949                ExcerptRange {
12950                    context: Point::new(0, 0)..Point::new(3, 0),
12951                    primary: None,
12952                },
12953                ExcerptRange {
12954                    context: Point::new(5, 0)..Point::new(7, 0),
12955                    primary: None,
12956                },
12957                ExcerptRange {
12958                    context: Point::new(9, 0)..Point::new(10, 4),
12959                    primary: None,
12960                },
12961            ],
12962            cx,
12963        );
12964        multibuffer.push_excerpts(
12965            buffer_2.clone(),
12966            [
12967                ExcerptRange {
12968                    context: Point::new(0, 0)..Point::new(3, 0),
12969                    primary: None,
12970                },
12971                ExcerptRange {
12972                    context: Point::new(5, 0)..Point::new(7, 0),
12973                    primary: None,
12974                },
12975                ExcerptRange {
12976                    context: Point::new(9, 0)..Point::new(10, 4),
12977                    primary: None,
12978                },
12979            ],
12980            cx,
12981        );
12982        multibuffer.push_excerpts(
12983            buffer_3.clone(),
12984            [
12985                ExcerptRange {
12986                    context: Point::new(0, 0)..Point::new(3, 0),
12987                    primary: None,
12988                },
12989                ExcerptRange {
12990                    context: Point::new(5, 0)..Point::new(7, 0),
12991                    primary: None,
12992                },
12993                ExcerptRange {
12994                    context: Point::new(9, 0)..Point::new(10, 4),
12995                    primary: None,
12996                },
12997            ],
12998            cx,
12999        );
13000        multibuffer
13001    });
13002
13003    let fs = FakeFs::new(cx.executor());
13004    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13005    let (editor, cx) = cx
13006        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13007    editor.update_in(cx, |editor, _window, cx| {
13008        for (buffer, diff_base) in [
13009            (buffer_1.clone(), base_text_1),
13010            (buffer_2.clone(), base_text_2),
13011            (buffer_3.clone(), base_text_3),
13012        ] {
13013            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13014            editor
13015                .buffer
13016                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13017        }
13018    });
13019    cx.executor().run_until_parked();
13020
13021    editor.update_in(cx, |editor, window, cx| {
13022        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}");
13023        editor.select_all(&SelectAll, window, cx);
13024        editor.git_restore(&Default::default(), window, cx);
13025    });
13026    cx.executor().run_until_parked();
13027
13028    // When all ranges are selected, all buffer hunks are reverted.
13029    editor.update(cx, |editor, cx| {
13030        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");
13031    });
13032    buffer_1.update(cx, |buffer, _| {
13033        assert_eq!(buffer.text(), base_text_1);
13034    });
13035    buffer_2.update(cx, |buffer, _| {
13036        assert_eq!(buffer.text(), base_text_2);
13037    });
13038    buffer_3.update(cx, |buffer, _| {
13039        assert_eq!(buffer.text(), base_text_3);
13040    });
13041
13042    editor.update_in(cx, |editor, window, cx| {
13043        editor.undo(&Default::default(), window, cx);
13044    });
13045
13046    editor.update_in(cx, |editor, window, cx| {
13047        editor.change_selections(None, window, cx, |s| {
13048            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13049        });
13050        editor.git_restore(&Default::default(), window, cx);
13051    });
13052
13053    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13054    // but not affect buffer_2 and its related excerpts.
13055    editor.update(cx, |editor, cx| {
13056        assert_eq!(
13057            editor.text(cx),
13058            "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}"
13059        );
13060    });
13061    buffer_1.update(cx, |buffer, _| {
13062        assert_eq!(buffer.text(), base_text_1);
13063    });
13064    buffer_2.update(cx, |buffer, _| {
13065        assert_eq!(
13066            buffer.text(),
13067            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13068        );
13069    });
13070    buffer_3.update(cx, |buffer, _| {
13071        assert_eq!(
13072            buffer.text(),
13073            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13074        );
13075    });
13076
13077    fn edit_first_char_of_every_line(text: &str) -> String {
13078        text.split('\n')
13079            .map(|line| format!("X{}", &line[1..]))
13080            .collect::<Vec<_>>()
13081            .join("\n")
13082    }
13083}
13084
13085#[gpui::test]
13086async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13087    init_test(cx, |_| {});
13088
13089    let cols = 4;
13090    let rows = 10;
13091    let sample_text_1 = sample_text(rows, cols, 'a');
13092    assert_eq!(
13093        sample_text_1,
13094        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13095    );
13096    let sample_text_2 = sample_text(rows, cols, 'l');
13097    assert_eq!(
13098        sample_text_2,
13099        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13100    );
13101    let sample_text_3 = sample_text(rows, cols, 'v');
13102    assert_eq!(
13103        sample_text_3,
13104        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13105    );
13106
13107    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13108    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13109    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13110
13111    let multi_buffer = cx.new(|cx| {
13112        let mut multibuffer = MultiBuffer::new(ReadWrite);
13113        multibuffer.push_excerpts(
13114            buffer_1.clone(),
13115            [
13116                ExcerptRange {
13117                    context: Point::new(0, 0)..Point::new(3, 0),
13118                    primary: None,
13119                },
13120                ExcerptRange {
13121                    context: Point::new(5, 0)..Point::new(7, 0),
13122                    primary: None,
13123                },
13124                ExcerptRange {
13125                    context: Point::new(9, 0)..Point::new(10, 4),
13126                    primary: None,
13127                },
13128            ],
13129            cx,
13130        );
13131        multibuffer.push_excerpts(
13132            buffer_2.clone(),
13133            [
13134                ExcerptRange {
13135                    context: Point::new(0, 0)..Point::new(3, 0),
13136                    primary: None,
13137                },
13138                ExcerptRange {
13139                    context: Point::new(5, 0)..Point::new(7, 0),
13140                    primary: None,
13141                },
13142                ExcerptRange {
13143                    context: Point::new(9, 0)..Point::new(10, 4),
13144                    primary: None,
13145                },
13146            ],
13147            cx,
13148        );
13149        multibuffer.push_excerpts(
13150            buffer_3.clone(),
13151            [
13152                ExcerptRange {
13153                    context: Point::new(0, 0)..Point::new(3, 0),
13154                    primary: None,
13155                },
13156                ExcerptRange {
13157                    context: Point::new(5, 0)..Point::new(7, 0),
13158                    primary: None,
13159                },
13160                ExcerptRange {
13161                    context: Point::new(9, 0)..Point::new(10, 4),
13162                    primary: None,
13163                },
13164            ],
13165            cx,
13166        );
13167        multibuffer
13168    });
13169
13170    let fs = FakeFs::new(cx.executor());
13171    fs.insert_tree(
13172        "/a",
13173        json!({
13174            "main.rs": sample_text_1,
13175            "other.rs": sample_text_2,
13176            "lib.rs": sample_text_3,
13177        }),
13178    )
13179    .await;
13180    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13181    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13182    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13183    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13184        Editor::new(
13185            EditorMode::Full,
13186            multi_buffer,
13187            Some(project.clone()),
13188            true,
13189            window,
13190            cx,
13191        )
13192    });
13193    let multibuffer_item_id = workspace
13194        .update(cx, |workspace, window, cx| {
13195            assert!(
13196                workspace.active_item(cx).is_none(),
13197                "active item should be None before the first item is added"
13198            );
13199            workspace.add_item_to_active_pane(
13200                Box::new(multi_buffer_editor.clone()),
13201                None,
13202                true,
13203                window,
13204                cx,
13205            );
13206            let active_item = workspace
13207                .active_item(cx)
13208                .expect("should have an active item after adding the multi buffer");
13209            assert!(
13210                !active_item.is_singleton(cx),
13211                "A multi buffer was expected to active after adding"
13212            );
13213            active_item.item_id()
13214        })
13215        .unwrap();
13216    cx.executor().run_until_parked();
13217
13218    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13219        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13220            s.select_ranges(Some(1..2))
13221        });
13222        editor.open_excerpts(&OpenExcerpts, window, cx);
13223    });
13224    cx.executor().run_until_parked();
13225    let first_item_id = workspace
13226        .update(cx, |workspace, window, cx| {
13227            let active_item = workspace
13228                .active_item(cx)
13229                .expect("should have an active item after navigating into the 1st buffer");
13230            let first_item_id = active_item.item_id();
13231            assert_ne!(
13232                first_item_id, multibuffer_item_id,
13233                "Should navigate into the 1st buffer and activate it"
13234            );
13235            assert!(
13236                active_item.is_singleton(cx),
13237                "New active item should be a singleton buffer"
13238            );
13239            assert_eq!(
13240                active_item
13241                    .act_as::<Editor>(cx)
13242                    .expect("should have navigated into an editor for the 1st buffer")
13243                    .read(cx)
13244                    .text(cx),
13245                sample_text_1
13246            );
13247
13248            workspace
13249                .go_back(workspace.active_pane().downgrade(), window, cx)
13250                .detach_and_log_err(cx);
13251
13252            first_item_id
13253        })
13254        .unwrap();
13255    cx.executor().run_until_parked();
13256    workspace
13257        .update(cx, |workspace, _, cx| {
13258            let active_item = workspace
13259                .active_item(cx)
13260                .expect("should have an active item after navigating back");
13261            assert_eq!(
13262                active_item.item_id(),
13263                multibuffer_item_id,
13264                "Should navigate back to the multi buffer"
13265            );
13266            assert!(!active_item.is_singleton(cx));
13267        })
13268        .unwrap();
13269
13270    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13271        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13272            s.select_ranges(Some(39..40))
13273        });
13274        editor.open_excerpts(&OpenExcerpts, window, cx);
13275    });
13276    cx.executor().run_until_parked();
13277    let second_item_id = workspace
13278        .update(cx, |workspace, window, cx| {
13279            let active_item = workspace
13280                .active_item(cx)
13281                .expect("should have an active item after navigating into the 2nd buffer");
13282            let second_item_id = active_item.item_id();
13283            assert_ne!(
13284                second_item_id, multibuffer_item_id,
13285                "Should navigate away from the multibuffer"
13286            );
13287            assert_ne!(
13288                second_item_id, first_item_id,
13289                "Should navigate into the 2nd buffer and activate it"
13290            );
13291            assert!(
13292                active_item.is_singleton(cx),
13293                "New active item should be a singleton buffer"
13294            );
13295            assert_eq!(
13296                active_item
13297                    .act_as::<Editor>(cx)
13298                    .expect("should have navigated into an editor")
13299                    .read(cx)
13300                    .text(cx),
13301                sample_text_2
13302            );
13303
13304            workspace
13305                .go_back(workspace.active_pane().downgrade(), window, cx)
13306                .detach_and_log_err(cx);
13307
13308            second_item_id
13309        })
13310        .unwrap();
13311    cx.executor().run_until_parked();
13312    workspace
13313        .update(cx, |workspace, _, cx| {
13314            let active_item = workspace
13315                .active_item(cx)
13316                .expect("should have an active item after navigating back from the 2nd buffer");
13317            assert_eq!(
13318                active_item.item_id(),
13319                multibuffer_item_id,
13320                "Should navigate back from the 2nd buffer to the multi buffer"
13321            );
13322            assert!(!active_item.is_singleton(cx));
13323        })
13324        .unwrap();
13325
13326    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13327        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13328            s.select_ranges(Some(70..70))
13329        });
13330        editor.open_excerpts(&OpenExcerpts, window, cx);
13331    });
13332    cx.executor().run_until_parked();
13333    workspace
13334        .update(cx, |workspace, window, cx| {
13335            let active_item = workspace
13336                .active_item(cx)
13337                .expect("should have an active item after navigating into the 3rd buffer");
13338            let third_item_id = active_item.item_id();
13339            assert_ne!(
13340                third_item_id, multibuffer_item_id,
13341                "Should navigate into the 3rd buffer and activate it"
13342            );
13343            assert_ne!(third_item_id, first_item_id);
13344            assert_ne!(third_item_id, second_item_id);
13345            assert!(
13346                active_item.is_singleton(cx),
13347                "New active item should be a singleton buffer"
13348            );
13349            assert_eq!(
13350                active_item
13351                    .act_as::<Editor>(cx)
13352                    .expect("should have navigated into an editor")
13353                    .read(cx)
13354                    .text(cx),
13355                sample_text_3
13356            );
13357
13358            workspace
13359                .go_back(workspace.active_pane().downgrade(), window, cx)
13360                .detach_and_log_err(cx);
13361        })
13362        .unwrap();
13363    cx.executor().run_until_parked();
13364    workspace
13365        .update(cx, |workspace, _, cx| {
13366            let active_item = workspace
13367                .active_item(cx)
13368                .expect("should have an active item after navigating back from the 3rd buffer");
13369            assert_eq!(
13370                active_item.item_id(),
13371                multibuffer_item_id,
13372                "Should navigate back from the 3rd buffer to the multi buffer"
13373            );
13374            assert!(!active_item.is_singleton(cx));
13375        })
13376        .unwrap();
13377}
13378
13379#[gpui::test]
13380async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13381    init_test(cx, |_| {});
13382
13383    let mut cx = EditorTestContext::new(cx).await;
13384
13385    let diff_base = r#"
13386        use some::mod;
13387
13388        const A: u32 = 42;
13389
13390        fn main() {
13391            println!("hello");
13392
13393            println!("world");
13394        }
13395        "#
13396    .unindent();
13397
13398    cx.set_state(
13399        &r#"
13400        use some::modified;
13401
13402        ˇ
13403        fn main() {
13404            println!("hello there");
13405
13406            println!("around the");
13407            println!("world");
13408        }
13409        "#
13410        .unindent(),
13411    );
13412
13413    cx.set_head_text(&diff_base);
13414    executor.run_until_parked();
13415
13416    cx.update_editor(|editor, window, cx| {
13417        editor.go_to_next_hunk(&GoToHunk, window, cx);
13418        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13419    });
13420    executor.run_until_parked();
13421    cx.assert_state_with_diff(
13422        r#"
13423          use some::modified;
13424
13425
13426          fn main() {
13427        -     println!("hello");
13428        + ˇ    println!("hello there");
13429
13430              println!("around the");
13431              println!("world");
13432          }
13433        "#
13434        .unindent(),
13435    );
13436
13437    cx.update_editor(|editor, window, cx| {
13438        for _ in 0..2 {
13439            editor.go_to_next_hunk(&GoToHunk, window, cx);
13440            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13441        }
13442    });
13443    executor.run_until_parked();
13444    cx.assert_state_with_diff(
13445        r#"
13446        - use some::mod;
13447        + ˇuse some::modified;
13448
13449
13450          fn main() {
13451        -     println!("hello");
13452        +     println!("hello there");
13453
13454        +     println!("around the");
13455              println!("world");
13456          }
13457        "#
13458        .unindent(),
13459    );
13460
13461    cx.update_editor(|editor, window, cx| {
13462        editor.go_to_next_hunk(&GoToHunk, window, cx);
13463        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13464    });
13465    executor.run_until_parked();
13466    cx.assert_state_with_diff(
13467        r#"
13468        - use some::mod;
13469        + use some::modified;
13470
13471        - const A: u32 = 42;
13472          ˇ
13473          fn main() {
13474        -     println!("hello");
13475        +     println!("hello there");
13476
13477        +     println!("around the");
13478              println!("world");
13479          }
13480        "#
13481        .unindent(),
13482    );
13483
13484    cx.update_editor(|editor, window, cx| {
13485        editor.cancel(&Cancel, window, cx);
13486    });
13487
13488    cx.assert_state_with_diff(
13489        r#"
13490          use some::modified;
13491
13492          ˇ
13493          fn main() {
13494              println!("hello there");
13495
13496              println!("around the");
13497              println!("world");
13498          }
13499        "#
13500        .unindent(),
13501    );
13502}
13503
13504#[gpui::test]
13505async fn test_diff_base_change_with_expanded_diff_hunks(
13506    executor: BackgroundExecutor,
13507    cx: &mut TestAppContext,
13508) {
13509    init_test(cx, |_| {});
13510
13511    let mut cx = EditorTestContext::new(cx).await;
13512
13513    let diff_base = r#"
13514        use some::mod1;
13515        use some::mod2;
13516
13517        const A: u32 = 42;
13518        const B: u32 = 42;
13519        const C: u32 = 42;
13520
13521        fn main() {
13522            println!("hello");
13523
13524            println!("world");
13525        }
13526        "#
13527    .unindent();
13528
13529    cx.set_state(
13530        &r#"
13531        use some::mod2;
13532
13533        const A: u32 = 42;
13534        const C: u32 = 42;
13535
13536        fn main(ˇ) {
13537            //println!("hello");
13538
13539            println!("world");
13540            //
13541            //
13542        }
13543        "#
13544        .unindent(),
13545    );
13546
13547    cx.set_head_text(&diff_base);
13548    executor.run_until_parked();
13549
13550    cx.update_editor(|editor, window, cx| {
13551        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13552    });
13553    executor.run_until_parked();
13554    cx.assert_state_with_diff(
13555        r#"
13556        - use some::mod1;
13557          use some::mod2;
13558
13559          const A: u32 = 42;
13560        - const B: u32 = 42;
13561          const C: u32 = 42;
13562
13563          fn main(ˇ) {
13564        -     println!("hello");
13565        +     //println!("hello");
13566
13567              println!("world");
13568        +     //
13569        +     //
13570          }
13571        "#
13572        .unindent(),
13573    );
13574
13575    cx.set_head_text("new diff base!");
13576    executor.run_until_parked();
13577    cx.assert_state_with_diff(
13578        r#"
13579        - new diff base!
13580        + use some::mod2;
13581        +
13582        + const A: u32 = 42;
13583        + const C: u32 = 42;
13584        +
13585        + fn main(ˇ) {
13586        +     //println!("hello");
13587        +
13588        +     println!("world");
13589        +     //
13590        +     //
13591        + }
13592        "#
13593        .unindent(),
13594    );
13595}
13596
13597#[gpui::test]
13598async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13599    init_test(cx, |_| {});
13600
13601    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13602    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13603    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13604    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13605    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13606    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13607
13608    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13609    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13610    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13611
13612    let multi_buffer = cx.new(|cx| {
13613        let mut multibuffer = MultiBuffer::new(ReadWrite);
13614        multibuffer.push_excerpts(
13615            buffer_1.clone(),
13616            [
13617                ExcerptRange {
13618                    context: Point::new(0, 0)..Point::new(3, 0),
13619                    primary: None,
13620                },
13621                ExcerptRange {
13622                    context: Point::new(5, 0)..Point::new(7, 0),
13623                    primary: None,
13624                },
13625                ExcerptRange {
13626                    context: Point::new(9, 0)..Point::new(10, 3),
13627                    primary: None,
13628                },
13629            ],
13630            cx,
13631        );
13632        multibuffer.push_excerpts(
13633            buffer_2.clone(),
13634            [
13635                ExcerptRange {
13636                    context: Point::new(0, 0)..Point::new(3, 0),
13637                    primary: None,
13638                },
13639                ExcerptRange {
13640                    context: Point::new(5, 0)..Point::new(7, 0),
13641                    primary: None,
13642                },
13643                ExcerptRange {
13644                    context: Point::new(9, 0)..Point::new(10, 3),
13645                    primary: None,
13646                },
13647            ],
13648            cx,
13649        );
13650        multibuffer.push_excerpts(
13651            buffer_3.clone(),
13652            [
13653                ExcerptRange {
13654                    context: Point::new(0, 0)..Point::new(3, 0),
13655                    primary: None,
13656                },
13657                ExcerptRange {
13658                    context: Point::new(5, 0)..Point::new(7, 0),
13659                    primary: None,
13660                },
13661                ExcerptRange {
13662                    context: Point::new(9, 0)..Point::new(10, 3),
13663                    primary: None,
13664                },
13665            ],
13666            cx,
13667        );
13668        multibuffer
13669    });
13670
13671    let editor = cx.add_window(|window, cx| {
13672        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13673    });
13674    editor
13675        .update(cx, |editor, _window, cx| {
13676            for (buffer, diff_base) in [
13677                (buffer_1.clone(), file_1_old),
13678                (buffer_2.clone(), file_2_old),
13679                (buffer_3.clone(), file_3_old),
13680            ] {
13681                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13682                editor
13683                    .buffer
13684                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13685            }
13686        })
13687        .unwrap();
13688
13689    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13690    cx.run_until_parked();
13691
13692    cx.assert_editor_state(
13693        &"
13694            ˇaaa
13695            ccc
13696            ddd
13697
13698            ggg
13699            hhh
13700
13701
13702            lll
13703            mmm
13704            NNN
13705
13706            qqq
13707            rrr
13708
13709            uuu
13710            111
13711            222
13712            333
13713
13714            666
13715            777
13716
13717            000
13718            !!!"
13719        .unindent(),
13720    );
13721
13722    cx.update_editor(|editor, window, cx| {
13723        editor.select_all(&SelectAll, window, cx);
13724        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13725    });
13726    cx.executor().run_until_parked();
13727
13728    cx.assert_state_with_diff(
13729        "
13730            «aaa
13731          - bbb
13732            ccc
13733            ddd
13734
13735            ggg
13736            hhh
13737
13738
13739            lll
13740            mmm
13741          - nnn
13742          + NNN
13743
13744            qqq
13745            rrr
13746
13747            uuu
13748            111
13749            222
13750            333
13751
13752          + 666
13753            777
13754
13755            000
13756            !!!ˇ»"
13757            .unindent(),
13758    );
13759}
13760
13761#[gpui::test]
13762async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13763    init_test(cx, |_| {});
13764
13765    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13766    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13767
13768    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13769    let multi_buffer = cx.new(|cx| {
13770        let mut multibuffer = MultiBuffer::new(ReadWrite);
13771        multibuffer.push_excerpts(
13772            buffer.clone(),
13773            [
13774                ExcerptRange {
13775                    context: Point::new(0, 0)..Point::new(2, 0),
13776                    primary: None,
13777                },
13778                ExcerptRange {
13779                    context: Point::new(4, 0)..Point::new(7, 0),
13780                    primary: None,
13781                },
13782                ExcerptRange {
13783                    context: Point::new(9, 0)..Point::new(10, 0),
13784                    primary: None,
13785                },
13786            ],
13787            cx,
13788        );
13789        multibuffer
13790    });
13791
13792    let editor = cx.add_window(|window, cx| {
13793        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13794    });
13795    editor
13796        .update(cx, |editor, _window, cx| {
13797            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13798            editor
13799                .buffer
13800                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13801        })
13802        .unwrap();
13803
13804    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13805    cx.run_until_parked();
13806
13807    cx.update_editor(|editor, window, cx| {
13808        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13809    });
13810    cx.executor().run_until_parked();
13811
13812    // When the start of a hunk coincides with the start of its excerpt,
13813    // the hunk is expanded. When the start of a a hunk is earlier than
13814    // the start of its excerpt, the hunk is not expanded.
13815    cx.assert_state_with_diff(
13816        "
13817            ˇaaa
13818          - bbb
13819          + BBB
13820
13821          - ddd
13822          - eee
13823          + DDD
13824          + EEE
13825            fff
13826
13827            iii
13828        "
13829        .unindent(),
13830    );
13831}
13832
13833#[gpui::test]
13834async fn test_edits_around_expanded_insertion_hunks(
13835    executor: BackgroundExecutor,
13836    cx: &mut TestAppContext,
13837) {
13838    init_test(cx, |_| {});
13839
13840    let mut cx = EditorTestContext::new(cx).await;
13841
13842    let diff_base = r#"
13843        use some::mod1;
13844        use some::mod2;
13845
13846        const A: u32 = 42;
13847
13848        fn main() {
13849            println!("hello");
13850
13851            println!("world");
13852        }
13853        "#
13854    .unindent();
13855    executor.run_until_parked();
13856    cx.set_state(
13857        &r#"
13858        use some::mod1;
13859        use some::mod2;
13860
13861        const A: u32 = 42;
13862        const B: u32 = 42;
13863        const C: u32 = 42;
13864        ˇ
13865
13866        fn main() {
13867            println!("hello");
13868
13869            println!("world");
13870        }
13871        "#
13872        .unindent(),
13873    );
13874
13875    cx.set_head_text(&diff_base);
13876    executor.run_until_parked();
13877
13878    cx.update_editor(|editor, window, cx| {
13879        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13880    });
13881    executor.run_until_parked();
13882
13883    cx.assert_state_with_diff(
13884        r#"
13885        use some::mod1;
13886        use some::mod2;
13887
13888        const A: u32 = 42;
13889      + const B: u32 = 42;
13890      + const C: u32 = 42;
13891      + ˇ
13892
13893        fn main() {
13894            println!("hello");
13895
13896            println!("world");
13897        }
13898      "#
13899        .unindent(),
13900    );
13901
13902    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13903    executor.run_until_parked();
13904
13905    cx.assert_state_with_diff(
13906        r#"
13907        use some::mod1;
13908        use some::mod2;
13909
13910        const A: u32 = 42;
13911      + const B: u32 = 42;
13912      + const C: u32 = 42;
13913      + const D: u32 = 42;
13914      + ˇ
13915
13916        fn main() {
13917            println!("hello");
13918
13919            println!("world");
13920        }
13921      "#
13922        .unindent(),
13923    );
13924
13925    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13926    executor.run_until_parked();
13927
13928    cx.assert_state_with_diff(
13929        r#"
13930        use some::mod1;
13931        use some::mod2;
13932
13933        const A: u32 = 42;
13934      + const B: u32 = 42;
13935      + const C: u32 = 42;
13936      + const D: u32 = 42;
13937      + const E: u32 = 42;
13938      + ˇ
13939
13940        fn main() {
13941            println!("hello");
13942
13943            println!("world");
13944        }
13945      "#
13946        .unindent(),
13947    );
13948
13949    cx.update_editor(|editor, window, cx| {
13950        editor.delete_line(&DeleteLine, window, cx);
13951    });
13952    executor.run_until_parked();
13953
13954    cx.assert_state_with_diff(
13955        r#"
13956        use some::mod1;
13957        use some::mod2;
13958
13959        const A: u32 = 42;
13960      + const B: u32 = 42;
13961      + const C: u32 = 42;
13962      + const D: u32 = 42;
13963      + const E: u32 = 42;
13964        ˇ
13965        fn main() {
13966            println!("hello");
13967
13968            println!("world");
13969        }
13970      "#
13971        .unindent(),
13972    );
13973
13974    cx.update_editor(|editor, window, cx| {
13975        editor.move_up(&MoveUp, window, cx);
13976        editor.delete_line(&DeleteLine, window, cx);
13977        editor.move_up(&MoveUp, window, cx);
13978        editor.delete_line(&DeleteLine, window, cx);
13979        editor.move_up(&MoveUp, window, cx);
13980        editor.delete_line(&DeleteLine, window, cx);
13981    });
13982    executor.run_until_parked();
13983    cx.assert_state_with_diff(
13984        r#"
13985        use some::mod1;
13986        use some::mod2;
13987
13988        const A: u32 = 42;
13989      + const B: u32 = 42;
13990        ˇ
13991        fn main() {
13992            println!("hello");
13993
13994            println!("world");
13995        }
13996      "#
13997        .unindent(),
13998    );
13999
14000    cx.update_editor(|editor, window, cx| {
14001        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14002        editor.delete_line(&DeleteLine, window, cx);
14003    });
14004    executor.run_until_parked();
14005    cx.assert_state_with_diff(
14006        r#"
14007        ˇ
14008        fn main() {
14009            println!("hello");
14010
14011            println!("world");
14012        }
14013      "#
14014        .unindent(),
14015    );
14016}
14017
14018#[gpui::test]
14019async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14020    init_test(cx, |_| {});
14021
14022    let mut cx = EditorTestContext::new(cx).await;
14023    cx.set_head_text(indoc! { "
14024        one
14025        two
14026        three
14027        four
14028        five
14029        "
14030    });
14031    cx.set_state(indoc! { "
14032        one
14033        ˇthree
14034        five
14035    "});
14036    cx.run_until_parked();
14037    cx.update_editor(|editor, window, cx| {
14038        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14039    });
14040    cx.assert_state_with_diff(
14041        indoc! { "
14042        one
14043      - two
14044        ˇthree
14045      - four
14046        five
14047    "}
14048        .to_string(),
14049    );
14050    cx.update_editor(|editor, window, cx| {
14051        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14052    });
14053
14054    cx.assert_state_with_diff(
14055        indoc! { "
14056        one
14057        ˇthree
14058        five
14059    "}
14060        .to_string(),
14061    );
14062
14063    cx.set_state(indoc! { "
14064        one
14065        ˇTWO
14066        three
14067        four
14068        five
14069    "});
14070    cx.run_until_parked();
14071    cx.update_editor(|editor, window, cx| {
14072        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14073    });
14074
14075    cx.assert_state_with_diff(
14076        indoc! { "
14077            one
14078          - two
14079          + ˇTWO
14080            three
14081            four
14082            five
14083        "}
14084        .to_string(),
14085    );
14086    cx.update_editor(|editor, window, cx| {
14087        editor.move_up(&Default::default(), window, cx);
14088        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14089    });
14090    cx.assert_state_with_diff(
14091        indoc! { "
14092            one
14093            ˇTWO
14094            three
14095            four
14096            five
14097        "}
14098        .to_string(),
14099    );
14100}
14101
14102#[gpui::test]
14103async fn test_edits_around_expanded_deletion_hunks(
14104    executor: BackgroundExecutor,
14105    cx: &mut TestAppContext,
14106) {
14107    init_test(cx, |_| {});
14108
14109    let mut cx = EditorTestContext::new(cx).await;
14110
14111    let diff_base = r#"
14112        use some::mod1;
14113        use some::mod2;
14114
14115        const A: u32 = 42;
14116        const B: u32 = 42;
14117        const C: u32 = 42;
14118
14119
14120        fn main() {
14121            println!("hello");
14122
14123            println!("world");
14124        }
14125    "#
14126    .unindent();
14127    executor.run_until_parked();
14128    cx.set_state(
14129        &r#"
14130        use some::mod1;
14131        use some::mod2;
14132
14133        ˇconst B: u32 = 42;
14134        const C: u32 = 42;
14135
14136
14137        fn main() {
14138            println!("hello");
14139
14140            println!("world");
14141        }
14142        "#
14143        .unindent(),
14144    );
14145
14146    cx.set_head_text(&diff_base);
14147    executor.run_until_parked();
14148
14149    cx.update_editor(|editor, window, cx| {
14150        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14151    });
14152    executor.run_until_parked();
14153
14154    cx.assert_state_with_diff(
14155        r#"
14156        use some::mod1;
14157        use some::mod2;
14158
14159      - const A: u32 = 42;
14160        ˇconst B: u32 = 42;
14161        const C: u32 = 42;
14162
14163
14164        fn main() {
14165            println!("hello");
14166
14167            println!("world");
14168        }
14169      "#
14170        .unindent(),
14171    );
14172
14173    cx.update_editor(|editor, window, cx| {
14174        editor.delete_line(&DeleteLine, window, cx);
14175    });
14176    executor.run_until_parked();
14177    cx.assert_state_with_diff(
14178        r#"
14179        use some::mod1;
14180        use some::mod2;
14181
14182      - const A: u32 = 42;
14183      - const B: u32 = 42;
14184        ˇconst C: u32 = 42;
14185
14186
14187        fn main() {
14188            println!("hello");
14189
14190            println!("world");
14191        }
14192      "#
14193        .unindent(),
14194    );
14195
14196    cx.update_editor(|editor, window, cx| {
14197        editor.delete_line(&DeleteLine, window, cx);
14198    });
14199    executor.run_until_parked();
14200    cx.assert_state_with_diff(
14201        r#"
14202        use some::mod1;
14203        use some::mod2;
14204
14205      - const A: u32 = 42;
14206      - const B: u32 = 42;
14207      - const C: u32 = 42;
14208        ˇ
14209
14210        fn main() {
14211            println!("hello");
14212
14213            println!("world");
14214        }
14215      "#
14216        .unindent(),
14217    );
14218
14219    cx.update_editor(|editor, window, cx| {
14220        editor.handle_input("replacement", window, cx);
14221    });
14222    executor.run_until_parked();
14223    cx.assert_state_with_diff(
14224        r#"
14225        use some::mod1;
14226        use some::mod2;
14227
14228      - const A: u32 = 42;
14229      - const B: u32 = 42;
14230      - const C: u32 = 42;
14231      -
14232      + replacementˇ
14233
14234        fn main() {
14235            println!("hello");
14236
14237            println!("world");
14238        }
14239      "#
14240        .unindent(),
14241    );
14242}
14243
14244#[gpui::test]
14245async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14246    init_test(cx, |_| {});
14247
14248    let mut cx = EditorTestContext::new(cx).await;
14249
14250    let base_text = r#"
14251        one
14252        two
14253        three
14254        four
14255        five
14256    "#
14257    .unindent();
14258    executor.run_until_parked();
14259    cx.set_state(
14260        &r#"
14261        one
14262        two
14263        fˇour
14264        five
14265        "#
14266        .unindent(),
14267    );
14268
14269    cx.set_head_text(&base_text);
14270    executor.run_until_parked();
14271
14272    cx.update_editor(|editor, window, cx| {
14273        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14274    });
14275    executor.run_until_parked();
14276
14277    cx.assert_state_with_diff(
14278        r#"
14279          one
14280          two
14281        - three
14282          fˇour
14283          five
14284        "#
14285        .unindent(),
14286    );
14287
14288    cx.update_editor(|editor, window, cx| {
14289        editor.backspace(&Backspace, window, cx);
14290        editor.backspace(&Backspace, window, cx);
14291    });
14292    executor.run_until_parked();
14293    cx.assert_state_with_diff(
14294        r#"
14295          one
14296          two
14297        - threeˇ
14298        - four
14299        + our
14300          five
14301        "#
14302        .unindent(),
14303    );
14304}
14305
14306#[gpui::test]
14307async fn test_edit_after_expanded_modification_hunk(
14308    executor: BackgroundExecutor,
14309    cx: &mut TestAppContext,
14310) {
14311    init_test(cx, |_| {});
14312
14313    let mut cx = EditorTestContext::new(cx).await;
14314
14315    let diff_base = r#"
14316        use some::mod1;
14317        use some::mod2;
14318
14319        const A: u32 = 42;
14320        const B: u32 = 42;
14321        const C: u32 = 42;
14322        const D: u32 = 42;
14323
14324
14325        fn main() {
14326            println!("hello");
14327
14328            println!("world");
14329        }"#
14330    .unindent();
14331
14332    cx.set_state(
14333        &r#"
14334        use some::mod1;
14335        use some::mod2;
14336
14337        const A: u32 = 42;
14338        const B: u32 = 42;
14339        const C: u32 = 43ˇ
14340        const D: u32 = 42;
14341
14342
14343        fn main() {
14344            println!("hello");
14345
14346            println!("world");
14347        }"#
14348        .unindent(),
14349    );
14350
14351    cx.set_head_text(&diff_base);
14352    executor.run_until_parked();
14353    cx.update_editor(|editor, window, cx| {
14354        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14355    });
14356    executor.run_until_parked();
14357
14358    cx.assert_state_with_diff(
14359        r#"
14360        use some::mod1;
14361        use some::mod2;
14362
14363        const A: u32 = 42;
14364        const B: u32 = 42;
14365      - const C: u32 = 42;
14366      + const C: u32 = 43ˇ
14367        const D: u32 = 42;
14368
14369
14370        fn main() {
14371            println!("hello");
14372
14373            println!("world");
14374        }"#
14375        .unindent(),
14376    );
14377
14378    cx.update_editor(|editor, window, cx| {
14379        editor.handle_input("\nnew_line\n", window, cx);
14380    });
14381    executor.run_until_parked();
14382
14383    cx.assert_state_with_diff(
14384        r#"
14385        use some::mod1;
14386        use some::mod2;
14387
14388        const A: u32 = 42;
14389        const B: u32 = 42;
14390      - const C: u32 = 42;
14391      + const C: u32 = 43
14392      + new_line
14393      + ˇ
14394        const D: u32 = 42;
14395
14396
14397        fn main() {
14398            println!("hello");
14399
14400            println!("world");
14401        }"#
14402        .unindent(),
14403    );
14404}
14405
14406#[gpui::test]
14407async fn test_stage_and_unstage_added_file_hunk(
14408    executor: BackgroundExecutor,
14409    cx: &mut TestAppContext,
14410) {
14411    init_test(cx, |_| {});
14412
14413    let mut cx = EditorTestContext::new(cx).await;
14414    cx.update_editor(|editor, _, cx| {
14415        editor.set_expand_all_diff_hunks(cx);
14416    });
14417
14418    let working_copy = r#"
14419            ˇfn main() {
14420                println!("hello, world!");
14421            }
14422        "#
14423    .unindent();
14424
14425    cx.set_state(&working_copy);
14426    executor.run_until_parked();
14427
14428    cx.assert_state_with_diff(
14429        r#"
14430            + ˇfn main() {
14431            +     println!("hello, world!");
14432            + }
14433        "#
14434        .unindent(),
14435    );
14436    cx.assert_index_text(None);
14437
14438    cx.update_editor(|editor, window, cx| {
14439        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14440    });
14441    executor.run_until_parked();
14442    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14443    cx.assert_state_with_diff(
14444        r#"
14445            + ˇfn main() {
14446            +     println!("hello, world!");
14447            + }
14448        "#
14449        .unindent(),
14450    );
14451
14452    cx.update_editor(|editor, window, cx| {
14453        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14454    });
14455    executor.run_until_parked();
14456    cx.assert_index_text(None);
14457}
14458
14459async fn setup_indent_guides_editor(
14460    text: &str,
14461    cx: &mut TestAppContext,
14462) -> (BufferId, EditorTestContext) {
14463    init_test(cx, |_| {});
14464
14465    let mut cx = EditorTestContext::new(cx).await;
14466
14467    let buffer_id = cx.update_editor(|editor, window, cx| {
14468        editor.set_text(text, window, cx);
14469        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14470
14471        buffer_ids[0]
14472    });
14473
14474    (buffer_id, cx)
14475}
14476
14477fn assert_indent_guides(
14478    range: Range<u32>,
14479    expected: Vec<IndentGuide>,
14480    active_indices: Option<Vec<usize>>,
14481    cx: &mut EditorTestContext,
14482) {
14483    let indent_guides = cx.update_editor(|editor, window, cx| {
14484        let snapshot = editor.snapshot(window, cx).display_snapshot;
14485        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14486            editor,
14487            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14488            true,
14489            &snapshot,
14490            cx,
14491        );
14492
14493        indent_guides.sort_by(|a, b| {
14494            a.depth.cmp(&b.depth).then(
14495                a.start_row
14496                    .cmp(&b.start_row)
14497                    .then(a.end_row.cmp(&b.end_row)),
14498            )
14499        });
14500        indent_guides
14501    });
14502
14503    if let Some(expected) = active_indices {
14504        let active_indices = cx.update_editor(|editor, window, cx| {
14505            let snapshot = editor.snapshot(window, cx).display_snapshot;
14506            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14507        });
14508
14509        assert_eq!(
14510            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14511            expected,
14512            "Active indent guide indices do not match"
14513        );
14514    }
14515
14516    assert_eq!(indent_guides, expected, "Indent guides do not match");
14517}
14518
14519fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14520    IndentGuide {
14521        buffer_id,
14522        start_row: MultiBufferRow(start_row),
14523        end_row: MultiBufferRow(end_row),
14524        depth,
14525        tab_size: 4,
14526        settings: IndentGuideSettings {
14527            enabled: true,
14528            line_width: 1,
14529            active_line_width: 1,
14530            ..Default::default()
14531        },
14532    }
14533}
14534
14535#[gpui::test]
14536async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14537    let (buffer_id, mut cx) = setup_indent_guides_editor(
14538        &"
14539    fn main() {
14540        let a = 1;
14541    }"
14542        .unindent(),
14543        cx,
14544    )
14545    .await;
14546
14547    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14548}
14549
14550#[gpui::test]
14551async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14552    let (buffer_id, mut cx) = setup_indent_guides_editor(
14553        &"
14554    fn main() {
14555        let a = 1;
14556        let b = 2;
14557    }"
14558        .unindent(),
14559        cx,
14560    )
14561    .await;
14562
14563    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14564}
14565
14566#[gpui::test]
14567async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14568    let (buffer_id, mut cx) = setup_indent_guides_editor(
14569        &"
14570    fn main() {
14571        let a = 1;
14572        if a == 3 {
14573            let b = 2;
14574        } else {
14575            let c = 3;
14576        }
14577    }"
14578        .unindent(),
14579        cx,
14580    )
14581    .await;
14582
14583    assert_indent_guides(
14584        0..8,
14585        vec![
14586            indent_guide(buffer_id, 1, 6, 0),
14587            indent_guide(buffer_id, 3, 3, 1),
14588            indent_guide(buffer_id, 5, 5, 1),
14589        ],
14590        None,
14591        &mut cx,
14592    );
14593}
14594
14595#[gpui::test]
14596async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14597    let (buffer_id, mut cx) = setup_indent_guides_editor(
14598        &"
14599    fn main() {
14600        let a = 1;
14601            let b = 2;
14602        let c = 3;
14603    }"
14604        .unindent(),
14605        cx,
14606    )
14607    .await;
14608
14609    assert_indent_guides(
14610        0..5,
14611        vec![
14612            indent_guide(buffer_id, 1, 3, 0),
14613            indent_guide(buffer_id, 2, 2, 1),
14614        ],
14615        None,
14616        &mut cx,
14617    );
14618}
14619
14620#[gpui::test]
14621async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14622    let (buffer_id, mut cx) = setup_indent_guides_editor(
14623        &"
14624        fn main() {
14625            let a = 1;
14626
14627            let c = 3;
14628        }"
14629        .unindent(),
14630        cx,
14631    )
14632    .await;
14633
14634    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14635}
14636
14637#[gpui::test]
14638async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14639    let (buffer_id, mut cx) = setup_indent_guides_editor(
14640        &"
14641        fn main() {
14642            let a = 1;
14643
14644            let c = 3;
14645
14646            if a == 3 {
14647                let b = 2;
14648            } else {
14649                let c = 3;
14650            }
14651        }"
14652        .unindent(),
14653        cx,
14654    )
14655    .await;
14656
14657    assert_indent_guides(
14658        0..11,
14659        vec![
14660            indent_guide(buffer_id, 1, 9, 0),
14661            indent_guide(buffer_id, 6, 6, 1),
14662            indent_guide(buffer_id, 8, 8, 1),
14663        ],
14664        None,
14665        &mut cx,
14666    );
14667}
14668
14669#[gpui::test]
14670async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14671    let (buffer_id, mut cx) = setup_indent_guides_editor(
14672        &"
14673        fn main() {
14674            let a = 1;
14675
14676            let c = 3;
14677
14678            if a == 3 {
14679                let b = 2;
14680            } else {
14681                let c = 3;
14682            }
14683        }"
14684        .unindent(),
14685        cx,
14686    )
14687    .await;
14688
14689    assert_indent_guides(
14690        1..11,
14691        vec![
14692            indent_guide(buffer_id, 1, 9, 0),
14693            indent_guide(buffer_id, 6, 6, 1),
14694            indent_guide(buffer_id, 8, 8, 1),
14695        ],
14696        None,
14697        &mut cx,
14698    );
14699}
14700
14701#[gpui::test]
14702async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14703    let (buffer_id, mut cx) = setup_indent_guides_editor(
14704        &"
14705        fn main() {
14706            let a = 1;
14707
14708            let c = 3;
14709
14710            if a == 3 {
14711                let b = 2;
14712            } else {
14713                let c = 3;
14714            }
14715        }"
14716        .unindent(),
14717        cx,
14718    )
14719    .await;
14720
14721    assert_indent_guides(
14722        1..10,
14723        vec![
14724            indent_guide(buffer_id, 1, 9, 0),
14725            indent_guide(buffer_id, 6, 6, 1),
14726            indent_guide(buffer_id, 8, 8, 1),
14727        ],
14728        None,
14729        &mut cx,
14730    );
14731}
14732
14733#[gpui::test]
14734async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14735    let (buffer_id, mut cx) = setup_indent_guides_editor(
14736        &"
14737        block1
14738            block2
14739                block3
14740                    block4
14741            block2
14742        block1
14743        block1"
14744            .unindent(),
14745        cx,
14746    )
14747    .await;
14748
14749    assert_indent_guides(
14750        1..10,
14751        vec![
14752            indent_guide(buffer_id, 1, 4, 0),
14753            indent_guide(buffer_id, 2, 3, 1),
14754            indent_guide(buffer_id, 3, 3, 2),
14755        ],
14756        None,
14757        &mut cx,
14758    );
14759}
14760
14761#[gpui::test]
14762async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14763    let (buffer_id, mut cx) = setup_indent_guides_editor(
14764        &"
14765        block1
14766            block2
14767                block3
14768
14769        block1
14770        block1"
14771            .unindent(),
14772        cx,
14773    )
14774    .await;
14775
14776    assert_indent_guides(
14777        0..6,
14778        vec![
14779            indent_guide(buffer_id, 1, 2, 0),
14780            indent_guide(buffer_id, 2, 2, 1),
14781        ],
14782        None,
14783        &mut cx,
14784    );
14785}
14786
14787#[gpui::test]
14788async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14789    let (buffer_id, mut cx) = setup_indent_guides_editor(
14790        &"
14791        block1
14792
14793
14794
14795            block2
14796        "
14797        .unindent(),
14798        cx,
14799    )
14800    .await;
14801
14802    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14803}
14804
14805#[gpui::test]
14806async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14807    let (buffer_id, mut cx) = setup_indent_guides_editor(
14808        &"
14809        def a:
14810        \tb = 3
14811        \tif True:
14812        \t\tc = 4
14813        \t\td = 5
14814        \tprint(b)
14815        "
14816        .unindent(),
14817        cx,
14818    )
14819    .await;
14820
14821    assert_indent_guides(
14822        0..6,
14823        vec![
14824            indent_guide(buffer_id, 1, 6, 0),
14825            indent_guide(buffer_id, 3, 4, 1),
14826        ],
14827        None,
14828        &mut cx,
14829    );
14830}
14831
14832#[gpui::test]
14833async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14834    let (buffer_id, mut cx) = setup_indent_guides_editor(
14835        &"
14836    fn main() {
14837        let a = 1;
14838    }"
14839        .unindent(),
14840        cx,
14841    )
14842    .await;
14843
14844    cx.update_editor(|editor, window, cx| {
14845        editor.change_selections(None, window, cx, |s| {
14846            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14847        });
14848    });
14849
14850    assert_indent_guides(
14851        0..3,
14852        vec![indent_guide(buffer_id, 1, 1, 0)],
14853        Some(vec![0]),
14854        &mut cx,
14855    );
14856}
14857
14858#[gpui::test]
14859async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14860    let (buffer_id, mut cx) = setup_indent_guides_editor(
14861        &"
14862    fn main() {
14863        if 1 == 2 {
14864            let a = 1;
14865        }
14866    }"
14867        .unindent(),
14868        cx,
14869    )
14870    .await;
14871
14872    cx.update_editor(|editor, window, cx| {
14873        editor.change_selections(None, window, cx, |s| {
14874            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14875        });
14876    });
14877
14878    assert_indent_guides(
14879        0..4,
14880        vec![
14881            indent_guide(buffer_id, 1, 3, 0),
14882            indent_guide(buffer_id, 2, 2, 1),
14883        ],
14884        Some(vec![1]),
14885        &mut cx,
14886    );
14887
14888    cx.update_editor(|editor, window, cx| {
14889        editor.change_selections(None, window, cx, |s| {
14890            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14891        });
14892    });
14893
14894    assert_indent_guides(
14895        0..4,
14896        vec![
14897            indent_guide(buffer_id, 1, 3, 0),
14898            indent_guide(buffer_id, 2, 2, 1),
14899        ],
14900        Some(vec![1]),
14901        &mut cx,
14902    );
14903
14904    cx.update_editor(|editor, window, cx| {
14905        editor.change_selections(None, window, cx, |s| {
14906            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14907        });
14908    });
14909
14910    assert_indent_guides(
14911        0..4,
14912        vec![
14913            indent_guide(buffer_id, 1, 3, 0),
14914            indent_guide(buffer_id, 2, 2, 1),
14915        ],
14916        Some(vec![0]),
14917        &mut cx,
14918    );
14919}
14920
14921#[gpui::test]
14922async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
14923    let (buffer_id, mut cx) = setup_indent_guides_editor(
14924        &"
14925    fn main() {
14926        let a = 1;
14927
14928        let b = 2;
14929    }"
14930        .unindent(),
14931        cx,
14932    )
14933    .await;
14934
14935    cx.update_editor(|editor, window, cx| {
14936        editor.change_selections(None, window, cx, |s| {
14937            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14938        });
14939    });
14940
14941    assert_indent_guides(
14942        0..5,
14943        vec![indent_guide(buffer_id, 1, 3, 0)],
14944        Some(vec![0]),
14945        &mut cx,
14946    );
14947}
14948
14949#[gpui::test]
14950async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
14951    let (buffer_id, mut cx) = setup_indent_guides_editor(
14952        &"
14953    def m:
14954        a = 1
14955        pass"
14956            .unindent(),
14957        cx,
14958    )
14959    .await;
14960
14961    cx.update_editor(|editor, window, cx| {
14962        editor.change_selections(None, window, cx, |s| {
14963            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14964        });
14965    });
14966
14967    assert_indent_guides(
14968        0..3,
14969        vec![indent_guide(buffer_id, 1, 2, 0)],
14970        Some(vec![0]),
14971        &mut cx,
14972    );
14973}
14974
14975#[gpui::test]
14976async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
14977    init_test(cx, |_| {});
14978    let mut cx = EditorTestContext::new(cx).await;
14979    let text = indoc! {
14980        "
14981        impl A {
14982            fn b() {
14983                0;
14984                3;
14985                5;
14986                6;
14987                7;
14988            }
14989        }
14990        "
14991    };
14992    let base_text = indoc! {
14993        "
14994        impl A {
14995            fn b() {
14996                0;
14997                1;
14998                2;
14999                3;
15000                4;
15001            }
15002            fn c() {
15003                5;
15004                6;
15005                7;
15006            }
15007        }
15008        "
15009    };
15010
15011    cx.update_editor(|editor, window, cx| {
15012        editor.set_text(text, window, cx);
15013
15014        editor.buffer().update(cx, |multibuffer, cx| {
15015            let buffer = multibuffer.as_singleton().unwrap();
15016            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15017
15018            multibuffer.set_all_diff_hunks_expanded(cx);
15019            multibuffer.add_diff(diff, cx);
15020
15021            buffer.read(cx).remote_id()
15022        })
15023    });
15024    cx.run_until_parked();
15025
15026    cx.assert_state_with_diff(
15027        indoc! { "
15028          impl A {
15029              fn b() {
15030                  0;
15031        -         1;
15032        -         2;
15033                  3;
15034        -         4;
15035        -     }
15036        -     fn c() {
15037                  5;
15038                  6;
15039                  7;
15040              }
15041          }
15042          ˇ"
15043        }
15044        .to_string(),
15045    );
15046
15047    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15048        editor
15049            .snapshot(window, cx)
15050            .buffer_snapshot
15051            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15052            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15053            .collect::<Vec<_>>()
15054    });
15055    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15056    assert_eq!(
15057        actual_guides,
15058        vec![
15059            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15060            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15061            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15062        ]
15063    );
15064}
15065
15066#[gpui::test]
15067async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15068    init_test(cx, |_| {});
15069    let mut cx = EditorTestContext::new(cx).await;
15070
15071    let diff_base = r#"
15072        a
15073        b
15074        c
15075        "#
15076    .unindent();
15077
15078    cx.set_state(
15079        &r#"
15080        ˇA
15081        b
15082        C
15083        "#
15084        .unindent(),
15085    );
15086    cx.set_head_text(&diff_base);
15087    cx.update_editor(|editor, window, cx| {
15088        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15089    });
15090    executor.run_until_parked();
15091
15092    let both_hunks_expanded = r#"
15093        - a
15094        + ˇA
15095          b
15096        - c
15097        + C
15098        "#
15099    .unindent();
15100
15101    cx.assert_state_with_diff(both_hunks_expanded.clone());
15102
15103    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15104        let snapshot = editor.snapshot(window, cx);
15105        let hunks = editor
15106            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15107            .collect::<Vec<_>>();
15108        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15109        let buffer_id = hunks[0].buffer_id;
15110        hunks
15111            .into_iter()
15112            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15113            .collect::<Vec<_>>()
15114    });
15115    assert_eq!(hunk_ranges.len(), 2);
15116
15117    cx.update_editor(|editor, _, cx| {
15118        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15119    });
15120    executor.run_until_parked();
15121
15122    let second_hunk_expanded = r#"
15123          ˇA
15124          b
15125        - c
15126        + C
15127        "#
15128    .unindent();
15129
15130    cx.assert_state_with_diff(second_hunk_expanded);
15131
15132    cx.update_editor(|editor, _, cx| {
15133        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15134    });
15135    executor.run_until_parked();
15136
15137    cx.assert_state_with_diff(both_hunks_expanded.clone());
15138
15139    cx.update_editor(|editor, _, cx| {
15140        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15141    });
15142    executor.run_until_parked();
15143
15144    let first_hunk_expanded = r#"
15145        - a
15146        + ˇA
15147          b
15148          C
15149        "#
15150    .unindent();
15151
15152    cx.assert_state_with_diff(first_hunk_expanded);
15153
15154    cx.update_editor(|editor, _, cx| {
15155        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15156    });
15157    executor.run_until_parked();
15158
15159    cx.assert_state_with_diff(both_hunks_expanded);
15160
15161    cx.set_state(
15162        &r#"
15163        ˇA
15164        b
15165        "#
15166        .unindent(),
15167    );
15168    cx.run_until_parked();
15169
15170    // TODO this cursor position seems bad
15171    cx.assert_state_with_diff(
15172        r#"
15173        - ˇa
15174        + A
15175          b
15176        "#
15177        .unindent(),
15178    );
15179
15180    cx.update_editor(|editor, window, cx| {
15181        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15182    });
15183
15184    cx.assert_state_with_diff(
15185        r#"
15186            - ˇa
15187            + A
15188              b
15189            - c
15190            "#
15191        .unindent(),
15192    );
15193
15194    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15195        let snapshot = editor.snapshot(window, cx);
15196        let hunks = editor
15197            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15198            .collect::<Vec<_>>();
15199        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15200        let buffer_id = hunks[0].buffer_id;
15201        hunks
15202            .into_iter()
15203            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15204            .collect::<Vec<_>>()
15205    });
15206    assert_eq!(hunk_ranges.len(), 2);
15207
15208    cx.update_editor(|editor, _, cx| {
15209        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15210    });
15211    executor.run_until_parked();
15212
15213    cx.assert_state_with_diff(
15214        r#"
15215        - ˇa
15216        + A
15217          b
15218        "#
15219        .unindent(),
15220    );
15221}
15222
15223#[gpui::test]
15224async fn test_toggle_deletion_hunk_at_start_of_file(
15225    executor: BackgroundExecutor,
15226    cx: &mut TestAppContext,
15227) {
15228    init_test(cx, |_| {});
15229    let mut cx = EditorTestContext::new(cx).await;
15230
15231    let diff_base = r#"
15232        a
15233        b
15234        c
15235        "#
15236    .unindent();
15237
15238    cx.set_state(
15239        &r#"
15240        ˇb
15241        c
15242        "#
15243        .unindent(),
15244    );
15245    cx.set_head_text(&diff_base);
15246    cx.update_editor(|editor, window, cx| {
15247        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15248    });
15249    executor.run_until_parked();
15250
15251    let hunk_expanded = r#"
15252        - a
15253          ˇb
15254          c
15255        "#
15256    .unindent();
15257
15258    cx.assert_state_with_diff(hunk_expanded.clone());
15259
15260    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15261        let snapshot = editor.snapshot(window, cx);
15262        let hunks = editor
15263            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15264            .collect::<Vec<_>>();
15265        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15266        let buffer_id = hunks[0].buffer_id;
15267        hunks
15268            .into_iter()
15269            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15270            .collect::<Vec<_>>()
15271    });
15272    assert_eq!(hunk_ranges.len(), 1);
15273
15274    cx.update_editor(|editor, _, cx| {
15275        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15276    });
15277    executor.run_until_parked();
15278
15279    let hunk_collapsed = r#"
15280          ˇb
15281          c
15282        "#
15283    .unindent();
15284
15285    cx.assert_state_with_diff(hunk_collapsed);
15286
15287    cx.update_editor(|editor, _, cx| {
15288        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15289    });
15290    executor.run_until_parked();
15291
15292    cx.assert_state_with_diff(hunk_expanded.clone());
15293}
15294
15295#[gpui::test]
15296async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15297    init_test(cx, |_| {});
15298
15299    let fs = FakeFs::new(cx.executor());
15300    fs.insert_tree(
15301        path!("/test"),
15302        json!({
15303            ".git": {},
15304            "file-1": "ONE\n",
15305            "file-2": "TWO\n",
15306            "file-3": "THREE\n",
15307        }),
15308    )
15309    .await;
15310
15311    fs.set_head_for_repo(
15312        path!("/test/.git").as_ref(),
15313        &[
15314            ("file-1".into(), "one\n".into()),
15315            ("file-2".into(), "two\n".into()),
15316            ("file-3".into(), "three\n".into()),
15317        ],
15318    );
15319
15320    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15321    let mut buffers = vec![];
15322    for i in 1..=3 {
15323        let buffer = project
15324            .update(cx, |project, cx| {
15325                let path = format!(path!("/test/file-{}"), i);
15326                project.open_local_buffer(path, cx)
15327            })
15328            .await
15329            .unwrap();
15330        buffers.push(buffer);
15331    }
15332
15333    let multibuffer = cx.new(|cx| {
15334        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15335        multibuffer.set_all_diff_hunks_expanded(cx);
15336        for buffer in &buffers {
15337            let snapshot = buffer.read(cx).snapshot();
15338            multibuffer.set_excerpts_for_path(
15339                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15340                buffer.clone(),
15341                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15342                DEFAULT_MULTIBUFFER_CONTEXT,
15343                cx,
15344            );
15345        }
15346        multibuffer
15347    });
15348
15349    let editor = cx.add_window(|window, cx| {
15350        Editor::new(
15351            EditorMode::Full,
15352            multibuffer,
15353            Some(project),
15354            true,
15355            window,
15356            cx,
15357        )
15358    });
15359    cx.run_until_parked();
15360
15361    let snapshot = editor
15362        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15363        .unwrap();
15364    let hunks = snapshot
15365        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15366        .map(|hunk| match hunk {
15367            DisplayDiffHunk::Unfolded {
15368                display_row_range, ..
15369            } => display_row_range,
15370            DisplayDiffHunk::Folded { .. } => unreachable!(),
15371        })
15372        .collect::<Vec<_>>();
15373    assert_eq!(
15374        hunks,
15375        [
15376            DisplayRow(3)..DisplayRow(5),
15377            DisplayRow(10)..DisplayRow(12),
15378            DisplayRow(17)..DisplayRow(19),
15379        ]
15380    );
15381}
15382
15383#[gpui::test]
15384async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15385    init_test(cx, |_| {});
15386
15387    let mut cx = EditorTestContext::new(cx).await;
15388    cx.set_head_text(indoc! { "
15389        one
15390        two
15391        three
15392        four
15393        five
15394        "
15395    });
15396    cx.set_index_text(indoc! { "
15397        one
15398        two
15399        three
15400        four
15401        five
15402        "
15403    });
15404    cx.set_state(indoc! {"
15405        one
15406        TWO
15407        ˇTHREE
15408        FOUR
15409        five
15410    "});
15411    cx.run_until_parked();
15412    cx.update_editor(|editor, window, cx| {
15413        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15414    });
15415    cx.run_until_parked();
15416    cx.assert_index_text(Some(indoc! {"
15417        one
15418        TWO
15419        THREE
15420        FOUR
15421        five
15422    "}));
15423    cx.set_state(indoc! { "
15424        one
15425        TWO
15426        ˇTHREE-HUNDRED
15427        FOUR
15428        five
15429    "});
15430    cx.run_until_parked();
15431    cx.update_editor(|editor, window, cx| {
15432        let snapshot = editor.snapshot(window, cx);
15433        let hunks = editor
15434            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15435            .collect::<Vec<_>>();
15436        assert_eq!(hunks.len(), 1);
15437        assert_eq!(
15438            hunks[0].status(),
15439            DiffHunkStatus {
15440                kind: DiffHunkStatusKind::Modified,
15441                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15442            }
15443        );
15444
15445        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15446    });
15447    cx.run_until_parked();
15448    cx.assert_index_text(Some(indoc! {"
15449        one
15450        TWO
15451        THREE-HUNDRED
15452        FOUR
15453        five
15454    "}));
15455}
15456
15457#[gpui::test]
15458fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15459    init_test(cx, |_| {});
15460
15461    let editor = cx.add_window(|window, cx| {
15462        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15463        build_editor(buffer, window, cx)
15464    });
15465
15466    let render_args = Arc::new(Mutex::new(None));
15467    let snapshot = editor
15468        .update(cx, |editor, window, cx| {
15469            let snapshot = editor.buffer().read(cx).snapshot(cx);
15470            let range =
15471                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15472
15473            struct RenderArgs {
15474                row: MultiBufferRow,
15475                folded: bool,
15476                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15477            }
15478
15479            let crease = Crease::inline(
15480                range,
15481                FoldPlaceholder::test(),
15482                {
15483                    let toggle_callback = render_args.clone();
15484                    move |row, folded, callback, _window, _cx| {
15485                        *toggle_callback.lock() = Some(RenderArgs {
15486                            row,
15487                            folded,
15488                            callback,
15489                        });
15490                        div()
15491                    }
15492                },
15493                |_row, _folded, _window, _cx| div(),
15494            );
15495
15496            editor.insert_creases(Some(crease), cx);
15497            let snapshot = editor.snapshot(window, cx);
15498            let _div = snapshot.render_crease_toggle(
15499                MultiBufferRow(1),
15500                false,
15501                cx.entity().clone(),
15502                window,
15503                cx,
15504            );
15505            snapshot
15506        })
15507        .unwrap();
15508
15509    let render_args = render_args.lock().take().unwrap();
15510    assert_eq!(render_args.row, MultiBufferRow(1));
15511    assert!(!render_args.folded);
15512    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15513
15514    cx.update_window(*editor, |_, window, cx| {
15515        (render_args.callback)(true, window, cx)
15516    })
15517    .unwrap();
15518    let snapshot = editor
15519        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15520        .unwrap();
15521    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15522
15523    cx.update_window(*editor, |_, window, cx| {
15524        (render_args.callback)(false, window, cx)
15525    })
15526    .unwrap();
15527    let snapshot = editor
15528        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15529        .unwrap();
15530    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15531}
15532
15533#[gpui::test]
15534async fn test_input_text(cx: &mut TestAppContext) {
15535    init_test(cx, |_| {});
15536    let mut cx = EditorTestContext::new(cx).await;
15537
15538    cx.set_state(
15539        &r#"ˇone
15540        two
15541
15542        three
15543        fourˇ
15544        five
15545
15546        siˇx"#
15547            .unindent(),
15548    );
15549
15550    cx.dispatch_action(HandleInput(String::new()));
15551    cx.assert_editor_state(
15552        &r#"ˇone
15553        two
15554
15555        three
15556        fourˇ
15557        five
15558
15559        siˇx"#
15560            .unindent(),
15561    );
15562
15563    cx.dispatch_action(HandleInput("AAAA".to_string()));
15564    cx.assert_editor_state(
15565        &r#"AAAAˇone
15566        two
15567
15568        three
15569        fourAAAAˇ
15570        five
15571
15572        siAAAAˇx"#
15573            .unindent(),
15574    );
15575}
15576
15577#[gpui::test]
15578async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15579    init_test(cx, |_| {});
15580
15581    let mut cx = EditorTestContext::new(cx).await;
15582    cx.set_state(
15583        r#"let foo = 1;
15584let foo = 2;
15585let foo = 3;
15586let fooˇ = 4;
15587let foo = 5;
15588let foo = 6;
15589let foo = 7;
15590let foo = 8;
15591let foo = 9;
15592let foo = 10;
15593let foo = 11;
15594let foo = 12;
15595let foo = 13;
15596let foo = 14;
15597let foo = 15;"#,
15598    );
15599
15600    cx.update_editor(|e, window, cx| {
15601        assert_eq!(
15602            e.next_scroll_position,
15603            NextScrollCursorCenterTopBottom::Center,
15604            "Default next scroll direction is center",
15605        );
15606
15607        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15608        assert_eq!(
15609            e.next_scroll_position,
15610            NextScrollCursorCenterTopBottom::Top,
15611            "After center, next scroll direction should be top",
15612        );
15613
15614        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15615        assert_eq!(
15616            e.next_scroll_position,
15617            NextScrollCursorCenterTopBottom::Bottom,
15618            "After top, next scroll direction should be bottom",
15619        );
15620
15621        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15622        assert_eq!(
15623            e.next_scroll_position,
15624            NextScrollCursorCenterTopBottom::Center,
15625            "After bottom, scrolling should start over",
15626        );
15627
15628        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15629        assert_eq!(
15630            e.next_scroll_position,
15631            NextScrollCursorCenterTopBottom::Top,
15632            "Scrolling continues if retriggered fast enough"
15633        );
15634    });
15635
15636    cx.executor()
15637        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15638    cx.executor().run_until_parked();
15639    cx.update_editor(|e, _, _| {
15640        assert_eq!(
15641            e.next_scroll_position,
15642            NextScrollCursorCenterTopBottom::Center,
15643            "If scrolling is not triggered fast enough, it should reset"
15644        );
15645    });
15646}
15647
15648#[gpui::test]
15649async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15650    init_test(cx, |_| {});
15651    let mut cx = EditorLspTestContext::new_rust(
15652        lsp::ServerCapabilities {
15653            definition_provider: Some(lsp::OneOf::Left(true)),
15654            references_provider: Some(lsp::OneOf::Left(true)),
15655            ..lsp::ServerCapabilities::default()
15656        },
15657        cx,
15658    )
15659    .await;
15660
15661    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15662        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15663            move |params, _| async move {
15664                if empty_go_to_definition {
15665                    Ok(None)
15666                } else {
15667                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15668                        uri: params.text_document_position_params.text_document.uri,
15669                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15670                    })))
15671                }
15672            },
15673        );
15674        let references =
15675            cx.lsp
15676                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15677                    Ok(Some(vec![lsp::Location {
15678                        uri: params.text_document_position.text_document.uri,
15679                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15680                    }]))
15681                });
15682        (go_to_definition, references)
15683    };
15684
15685    cx.set_state(
15686        &r#"fn one() {
15687            let mut a = ˇtwo();
15688        }
15689
15690        fn two() {}"#
15691            .unindent(),
15692    );
15693    set_up_lsp_handlers(false, &mut cx);
15694    let navigated = cx
15695        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15696        .await
15697        .expect("Failed to navigate to definition");
15698    assert_eq!(
15699        navigated,
15700        Navigated::Yes,
15701        "Should have navigated to definition from the GetDefinition response"
15702    );
15703    cx.assert_editor_state(
15704        &r#"fn one() {
15705            let mut a = two();
15706        }
15707
15708        fn «twoˇ»() {}"#
15709            .unindent(),
15710    );
15711
15712    let editors = cx.update_workspace(|workspace, _, cx| {
15713        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15714    });
15715    cx.update_editor(|_, _, test_editor_cx| {
15716        assert_eq!(
15717            editors.len(),
15718            1,
15719            "Initially, only one, test, editor should be open in the workspace"
15720        );
15721        assert_eq!(
15722            test_editor_cx.entity(),
15723            editors.last().expect("Asserted len is 1").clone()
15724        );
15725    });
15726
15727    set_up_lsp_handlers(true, &mut cx);
15728    let navigated = cx
15729        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15730        .await
15731        .expect("Failed to navigate to lookup references");
15732    assert_eq!(
15733        navigated,
15734        Navigated::Yes,
15735        "Should have navigated to references as a fallback after empty GoToDefinition response"
15736    );
15737    // We should not change the selections in the existing file,
15738    // if opening another milti buffer with the references
15739    cx.assert_editor_state(
15740        &r#"fn one() {
15741            let mut a = two();
15742        }
15743
15744        fn «twoˇ»() {}"#
15745            .unindent(),
15746    );
15747    let editors = cx.update_workspace(|workspace, _, cx| {
15748        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15749    });
15750    cx.update_editor(|_, _, test_editor_cx| {
15751        assert_eq!(
15752            editors.len(),
15753            2,
15754            "After falling back to references search, we open a new editor with the results"
15755        );
15756        let references_fallback_text = editors
15757            .into_iter()
15758            .find(|new_editor| *new_editor != test_editor_cx.entity())
15759            .expect("Should have one non-test editor now")
15760            .read(test_editor_cx)
15761            .text(test_editor_cx);
15762        assert_eq!(
15763            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15764            "Should use the range from the references response and not the GoToDefinition one"
15765        );
15766    });
15767}
15768
15769#[gpui::test]
15770async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15771    init_test(cx, |_| {});
15772
15773    let language = Arc::new(Language::new(
15774        LanguageConfig::default(),
15775        Some(tree_sitter_rust::LANGUAGE.into()),
15776    ));
15777
15778    let text = r#"
15779        #[cfg(test)]
15780        mod tests() {
15781            #[test]
15782            fn runnable_1() {
15783                let a = 1;
15784            }
15785
15786            #[test]
15787            fn runnable_2() {
15788                let a = 1;
15789                let b = 2;
15790            }
15791        }
15792    "#
15793    .unindent();
15794
15795    let fs = FakeFs::new(cx.executor());
15796    fs.insert_file("/file.rs", Default::default()).await;
15797
15798    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15799    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15800    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15801    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15802    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15803
15804    let editor = cx.new_window_entity(|window, cx| {
15805        Editor::new(
15806            EditorMode::Full,
15807            multi_buffer,
15808            Some(project.clone()),
15809            true,
15810            window,
15811            cx,
15812        )
15813    });
15814
15815    editor.update_in(cx, |editor, window, cx| {
15816        let snapshot = editor.buffer().read(cx).snapshot(cx);
15817        editor.tasks.insert(
15818            (buffer.read(cx).remote_id(), 3),
15819            RunnableTasks {
15820                templates: vec![],
15821                offset: snapshot.anchor_before(43),
15822                column: 0,
15823                extra_variables: HashMap::default(),
15824                context_range: BufferOffset(43)..BufferOffset(85),
15825            },
15826        );
15827        editor.tasks.insert(
15828            (buffer.read(cx).remote_id(), 8),
15829            RunnableTasks {
15830                templates: vec![],
15831                offset: snapshot.anchor_before(86),
15832                column: 0,
15833                extra_variables: HashMap::default(),
15834                context_range: BufferOffset(86)..BufferOffset(191),
15835            },
15836        );
15837
15838        // Test finding task when cursor is inside function body
15839        editor.change_selections(None, window, cx, |s| {
15840            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15841        });
15842        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15843        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15844
15845        // Test finding task when cursor is on function name
15846        editor.change_selections(None, window, cx, |s| {
15847            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15848        });
15849        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15850        assert_eq!(row, 8, "Should find task when cursor is on function name");
15851    });
15852}
15853
15854#[gpui::test]
15855async fn test_folding_buffers(cx: &mut TestAppContext) {
15856    init_test(cx, |_| {});
15857
15858    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15859    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15860    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15861
15862    let fs = FakeFs::new(cx.executor());
15863    fs.insert_tree(
15864        path!("/a"),
15865        json!({
15866            "first.rs": sample_text_1,
15867            "second.rs": sample_text_2,
15868            "third.rs": sample_text_3,
15869        }),
15870    )
15871    .await;
15872    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15873    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15874    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15875    let worktree = project.update(cx, |project, cx| {
15876        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15877        assert_eq!(worktrees.len(), 1);
15878        worktrees.pop().unwrap()
15879    });
15880    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15881
15882    let buffer_1 = project
15883        .update(cx, |project, cx| {
15884            project.open_buffer((worktree_id, "first.rs"), cx)
15885        })
15886        .await
15887        .unwrap();
15888    let buffer_2 = project
15889        .update(cx, |project, cx| {
15890            project.open_buffer((worktree_id, "second.rs"), cx)
15891        })
15892        .await
15893        .unwrap();
15894    let buffer_3 = project
15895        .update(cx, |project, cx| {
15896            project.open_buffer((worktree_id, "third.rs"), cx)
15897        })
15898        .await
15899        .unwrap();
15900
15901    let multi_buffer = cx.new(|cx| {
15902        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15903        multi_buffer.push_excerpts(
15904            buffer_1.clone(),
15905            [
15906                ExcerptRange {
15907                    context: Point::new(0, 0)..Point::new(3, 0),
15908                    primary: None,
15909                },
15910                ExcerptRange {
15911                    context: Point::new(5, 0)..Point::new(7, 0),
15912                    primary: None,
15913                },
15914                ExcerptRange {
15915                    context: Point::new(9, 0)..Point::new(10, 4),
15916                    primary: None,
15917                },
15918            ],
15919            cx,
15920        );
15921        multi_buffer.push_excerpts(
15922            buffer_2.clone(),
15923            [
15924                ExcerptRange {
15925                    context: Point::new(0, 0)..Point::new(3, 0),
15926                    primary: None,
15927                },
15928                ExcerptRange {
15929                    context: Point::new(5, 0)..Point::new(7, 0),
15930                    primary: None,
15931                },
15932                ExcerptRange {
15933                    context: Point::new(9, 0)..Point::new(10, 4),
15934                    primary: None,
15935                },
15936            ],
15937            cx,
15938        );
15939        multi_buffer.push_excerpts(
15940            buffer_3.clone(),
15941            [
15942                ExcerptRange {
15943                    context: Point::new(0, 0)..Point::new(3, 0),
15944                    primary: None,
15945                },
15946                ExcerptRange {
15947                    context: Point::new(5, 0)..Point::new(7, 0),
15948                    primary: None,
15949                },
15950                ExcerptRange {
15951                    context: Point::new(9, 0)..Point::new(10, 4),
15952                    primary: None,
15953                },
15954            ],
15955            cx,
15956        );
15957        multi_buffer
15958    });
15959    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15960        Editor::new(
15961            EditorMode::Full,
15962            multi_buffer.clone(),
15963            Some(project.clone()),
15964            true,
15965            window,
15966            cx,
15967        )
15968    });
15969
15970    assert_eq!(
15971        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15972        "\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",
15973    );
15974
15975    multi_buffer_editor.update(cx, |editor, cx| {
15976        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15977    });
15978    assert_eq!(
15979        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15980        "\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",
15981        "After folding the first buffer, its text should not be displayed"
15982    );
15983
15984    multi_buffer_editor.update(cx, |editor, cx| {
15985        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15986    });
15987    assert_eq!(
15988        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15989        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15990        "After folding the second buffer, its text should not be displayed"
15991    );
15992
15993    multi_buffer_editor.update(cx, |editor, cx| {
15994        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15995    });
15996    assert_eq!(
15997        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15998        "\n\n\n\n\n",
15999        "After folding the third buffer, its text should not be displayed"
16000    );
16001
16002    // Emulate selection inside the fold logic, that should work
16003    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16004        editor
16005            .snapshot(window, cx)
16006            .next_line_boundary(Point::new(0, 4));
16007    });
16008
16009    multi_buffer_editor.update(cx, |editor, cx| {
16010        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16011    });
16012    assert_eq!(
16013        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16014        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
16015        "After unfolding the second buffer, its text should be displayed"
16016    );
16017
16018    // Typing inside of buffer 1 causes that buffer to be unfolded.
16019    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16020        assert_eq!(
16021            multi_buffer
16022                .read(cx)
16023                .snapshot(cx)
16024                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16025                .collect::<String>(),
16026            "bbbb"
16027        );
16028        editor.change_selections(None, window, cx, |selections| {
16029            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16030        });
16031        editor.handle_input("B", window, cx);
16032    });
16033
16034    assert_eq!(
16035        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16036        "\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",
16037        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16038    );
16039
16040    multi_buffer_editor.update(cx, |editor, cx| {
16041        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16042    });
16043    assert_eq!(
16044        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16045        "\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",
16046        "After unfolding the all buffers, all original text should be displayed"
16047    );
16048}
16049
16050#[gpui::test]
16051async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16052    init_test(cx, |_| {});
16053
16054    let sample_text_1 = "1111\n2222\n3333".to_string();
16055    let sample_text_2 = "4444\n5555\n6666".to_string();
16056    let sample_text_3 = "7777\n8888\n9999".to_string();
16057
16058    let fs = FakeFs::new(cx.executor());
16059    fs.insert_tree(
16060        path!("/a"),
16061        json!({
16062            "first.rs": sample_text_1,
16063            "second.rs": sample_text_2,
16064            "third.rs": sample_text_3,
16065        }),
16066    )
16067    .await;
16068    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16069    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16070    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16071    let worktree = project.update(cx, |project, cx| {
16072        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16073        assert_eq!(worktrees.len(), 1);
16074        worktrees.pop().unwrap()
16075    });
16076    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16077
16078    let buffer_1 = project
16079        .update(cx, |project, cx| {
16080            project.open_buffer((worktree_id, "first.rs"), cx)
16081        })
16082        .await
16083        .unwrap();
16084    let buffer_2 = project
16085        .update(cx, |project, cx| {
16086            project.open_buffer((worktree_id, "second.rs"), cx)
16087        })
16088        .await
16089        .unwrap();
16090    let buffer_3 = project
16091        .update(cx, |project, cx| {
16092            project.open_buffer((worktree_id, "third.rs"), cx)
16093        })
16094        .await
16095        .unwrap();
16096
16097    let multi_buffer = cx.new(|cx| {
16098        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16099        multi_buffer.push_excerpts(
16100            buffer_1.clone(),
16101            [ExcerptRange {
16102                context: Point::new(0, 0)..Point::new(3, 0),
16103                primary: None,
16104            }],
16105            cx,
16106        );
16107        multi_buffer.push_excerpts(
16108            buffer_2.clone(),
16109            [ExcerptRange {
16110                context: Point::new(0, 0)..Point::new(3, 0),
16111                primary: None,
16112            }],
16113            cx,
16114        );
16115        multi_buffer.push_excerpts(
16116            buffer_3.clone(),
16117            [ExcerptRange {
16118                context: Point::new(0, 0)..Point::new(3, 0),
16119                primary: None,
16120            }],
16121            cx,
16122        );
16123        multi_buffer
16124    });
16125
16126    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16127        Editor::new(
16128            EditorMode::Full,
16129            multi_buffer,
16130            Some(project.clone()),
16131            true,
16132            window,
16133            cx,
16134        )
16135    });
16136
16137    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
16138    assert_eq!(
16139        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16140        full_text,
16141    );
16142
16143    multi_buffer_editor.update(cx, |editor, cx| {
16144        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16145    });
16146    assert_eq!(
16147        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16148        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
16149        "After folding the first buffer, its text should not be displayed"
16150    );
16151
16152    multi_buffer_editor.update(cx, |editor, cx| {
16153        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16154    });
16155
16156    assert_eq!(
16157        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16158        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
16159        "After folding the second buffer, its text should not be displayed"
16160    );
16161
16162    multi_buffer_editor.update(cx, |editor, cx| {
16163        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16164    });
16165    assert_eq!(
16166        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16167        "\n\n\n\n\n",
16168        "After folding the third buffer, its text should not be displayed"
16169    );
16170
16171    multi_buffer_editor.update(cx, |editor, cx| {
16172        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16173    });
16174    assert_eq!(
16175        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16176        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
16177        "After unfolding the second buffer, its text should be displayed"
16178    );
16179
16180    multi_buffer_editor.update(cx, |editor, cx| {
16181        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16182    });
16183    assert_eq!(
16184        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16185        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
16186        "After unfolding the first buffer, its text should be displayed"
16187    );
16188
16189    multi_buffer_editor.update(cx, |editor, cx| {
16190        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16191    });
16192    assert_eq!(
16193        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16194        full_text,
16195        "After unfolding all buffers, all original text should be displayed"
16196    );
16197}
16198
16199#[gpui::test]
16200async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16201    init_test(cx, |_| {});
16202
16203    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16204
16205    let fs = FakeFs::new(cx.executor());
16206    fs.insert_tree(
16207        path!("/a"),
16208        json!({
16209            "main.rs": sample_text,
16210        }),
16211    )
16212    .await;
16213    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16214    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16215    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16216    let worktree = project.update(cx, |project, cx| {
16217        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16218        assert_eq!(worktrees.len(), 1);
16219        worktrees.pop().unwrap()
16220    });
16221    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16222
16223    let buffer_1 = project
16224        .update(cx, |project, cx| {
16225            project.open_buffer((worktree_id, "main.rs"), cx)
16226        })
16227        .await
16228        .unwrap();
16229
16230    let multi_buffer = cx.new(|cx| {
16231        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16232        multi_buffer.push_excerpts(
16233            buffer_1.clone(),
16234            [ExcerptRange {
16235                context: Point::new(0, 0)
16236                    ..Point::new(
16237                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16238                        0,
16239                    ),
16240                primary: None,
16241            }],
16242            cx,
16243        );
16244        multi_buffer
16245    });
16246    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16247        Editor::new(
16248            EditorMode::Full,
16249            multi_buffer,
16250            Some(project.clone()),
16251            true,
16252            window,
16253            cx,
16254        )
16255    });
16256
16257    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16258    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16259        enum TestHighlight {}
16260        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16261        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16262        editor.highlight_text::<TestHighlight>(
16263            vec![highlight_range.clone()],
16264            HighlightStyle::color(Hsla::green()),
16265            cx,
16266        );
16267        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16268    });
16269
16270    let full_text = format!("\n\n\n{sample_text}\n");
16271    assert_eq!(
16272        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16273        full_text,
16274    );
16275}
16276
16277#[gpui::test]
16278async fn test_inline_completion_text(cx: &mut TestAppContext) {
16279    init_test(cx, |_| {});
16280
16281    // Simple insertion
16282    assert_highlighted_edits(
16283        "Hello, world!",
16284        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16285        true,
16286        cx,
16287        |highlighted_edits, cx| {
16288            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16289            assert_eq!(highlighted_edits.highlights.len(), 1);
16290            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16291            assert_eq!(
16292                highlighted_edits.highlights[0].1.background_color,
16293                Some(cx.theme().status().created_background)
16294            );
16295        },
16296    )
16297    .await;
16298
16299    // Replacement
16300    assert_highlighted_edits(
16301        "This is a test.",
16302        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16303        false,
16304        cx,
16305        |highlighted_edits, cx| {
16306            assert_eq!(highlighted_edits.text, "That is a test.");
16307            assert_eq!(highlighted_edits.highlights.len(), 1);
16308            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16309            assert_eq!(
16310                highlighted_edits.highlights[0].1.background_color,
16311                Some(cx.theme().status().created_background)
16312            );
16313        },
16314    )
16315    .await;
16316
16317    // Multiple edits
16318    assert_highlighted_edits(
16319        "Hello, world!",
16320        vec![
16321            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16322            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16323        ],
16324        false,
16325        cx,
16326        |highlighted_edits, cx| {
16327            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16328            assert_eq!(highlighted_edits.highlights.len(), 2);
16329            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16330            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16331            assert_eq!(
16332                highlighted_edits.highlights[0].1.background_color,
16333                Some(cx.theme().status().created_background)
16334            );
16335            assert_eq!(
16336                highlighted_edits.highlights[1].1.background_color,
16337                Some(cx.theme().status().created_background)
16338            );
16339        },
16340    )
16341    .await;
16342
16343    // Multiple lines with edits
16344    assert_highlighted_edits(
16345        "First line\nSecond line\nThird line\nFourth line",
16346        vec![
16347            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16348            (
16349                Point::new(2, 0)..Point::new(2, 10),
16350                "New third line".to_string(),
16351            ),
16352            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16353        ],
16354        false,
16355        cx,
16356        |highlighted_edits, cx| {
16357            assert_eq!(
16358                highlighted_edits.text,
16359                "Second modified\nNew third line\nFourth updated line"
16360            );
16361            assert_eq!(highlighted_edits.highlights.len(), 3);
16362            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16363            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16364            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16365            for highlight in &highlighted_edits.highlights {
16366                assert_eq!(
16367                    highlight.1.background_color,
16368                    Some(cx.theme().status().created_background)
16369                );
16370            }
16371        },
16372    )
16373    .await;
16374}
16375
16376#[gpui::test]
16377async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16378    init_test(cx, |_| {});
16379
16380    // Deletion
16381    assert_highlighted_edits(
16382        "Hello, world!",
16383        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16384        true,
16385        cx,
16386        |highlighted_edits, cx| {
16387            assert_eq!(highlighted_edits.text, "Hello, world!");
16388            assert_eq!(highlighted_edits.highlights.len(), 1);
16389            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16390            assert_eq!(
16391                highlighted_edits.highlights[0].1.background_color,
16392                Some(cx.theme().status().deleted_background)
16393            );
16394        },
16395    )
16396    .await;
16397
16398    // Insertion
16399    assert_highlighted_edits(
16400        "Hello, world!",
16401        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16402        true,
16403        cx,
16404        |highlighted_edits, cx| {
16405            assert_eq!(highlighted_edits.highlights.len(), 1);
16406            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16407            assert_eq!(
16408                highlighted_edits.highlights[0].1.background_color,
16409                Some(cx.theme().status().created_background)
16410            );
16411        },
16412    )
16413    .await;
16414}
16415
16416async fn assert_highlighted_edits(
16417    text: &str,
16418    edits: Vec<(Range<Point>, String)>,
16419    include_deletions: bool,
16420    cx: &mut TestAppContext,
16421    assertion_fn: impl Fn(HighlightedText, &App),
16422) {
16423    let window = cx.add_window(|window, cx| {
16424        let buffer = MultiBuffer::build_simple(text, cx);
16425        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
16426    });
16427    let cx = &mut VisualTestContext::from_window(*window, cx);
16428
16429    let (buffer, snapshot) = window
16430        .update(cx, |editor, _window, cx| {
16431            (
16432                editor.buffer().clone(),
16433                editor.buffer().read(cx).snapshot(cx),
16434            )
16435        })
16436        .unwrap();
16437
16438    let edits = edits
16439        .into_iter()
16440        .map(|(range, edit)| {
16441            (
16442                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16443                edit,
16444            )
16445        })
16446        .collect::<Vec<_>>();
16447
16448    let text_anchor_edits = edits
16449        .clone()
16450        .into_iter()
16451        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16452        .collect::<Vec<_>>();
16453
16454    let edit_preview = window
16455        .update(cx, |_, _window, cx| {
16456            buffer
16457                .read(cx)
16458                .as_singleton()
16459                .unwrap()
16460                .read(cx)
16461                .preview_edits(text_anchor_edits.into(), cx)
16462        })
16463        .unwrap()
16464        .await;
16465
16466    cx.update(|_window, cx| {
16467        let highlighted_edits = inline_completion_edit_text(
16468            &snapshot.as_singleton().unwrap().2,
16469            &edits,
16470            &edit_preview,
16471            include_deletions,
16472            cx,
16473        );
16474        assertion_fn(highlighted_edits, cx)
16475    });
16476}
16477
16478#[gpui::test]
16479async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16480    init_test(cx, |_| {});
16481    let capabilities = lsp::ServerCapabilities {
16482        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16483            prepare_provider: Some(true),
16484            work_done_progress_options: Default::default(),
16485        })),
16486        ..Default::default()
16487    };
16488    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16489
16490    cx.set_state(indoc! {"
16491        struct Fˇoo {}
16492    "});
16493
16494    cx.update_editor(|editor, _, cx| {
16495        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16496        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16497        editor.highlight_background::<DocumentHighlightRead>(
16498            &[highlight_range],
16499            |c| c.editor_document_highlight_read_background,
16500            cx,
16501        );
16502    });
16503
16504    let mut prepare_rename_handler =
16505        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16506            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16507                start: lsp::Position {
16508                    line: 0,
16509                    character: 7,
16510                },
16511                end: lsp::Position {
16512                    line: 0,
16513                    character: 10,
16514                },
16515            })))
16516        });
16517    let prepare_rename_task = cx
16518        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16519        .expect("Prepare rename was not started");
16520    prepare_rename_handler.next().await.unwrap();
16521    prepare_rename_task.await.expect("Prepare rename failed");
16522
16523    let mut rename_handler =
16524        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16525            let edit = lsp::TextEdit {
16526                range: lsp::Range {
16527                    start: lsp::Position {
16528                        line: 0,
16529                        character: 7,
16530                    },
16531                    end: lsp::Position {
16532                        line: 0,
16533                        character: 10,
16534                    },
16535                },
16536                new_text: "FooRenamed".to_string(),
16537            };
16538            Ok(Some(lsp::WorkspaceEdit::new(
16539                // Specify the same edit twice
16540                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16541            )))
16542        });
16543    let rename_task = cx
16544        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16545        .expect("Confirm rename was not started");
16546    rename_handler.next().await.unwrap();
16547    rename_task.await.expect("Confirm rename failed");
16548    cx.run_until_parked();
16549
16550    // Despite two edits, only one is actually applied as those are identical
16551    cx.assert_editor_state(indoc! {"
16552        struct FooRenamedˇ {}
16553    "});
16554}
16555
16556#[gpui::test]
16557async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16558    init_test(cx, |_| {});
16559    // These capabilities indicate that the server does not support prepare rename.
16560    let capabilities = lsp::ServerCapabilities {
16561        rename_provider: Some(lsp::OneOf::Left(true)),
16562        ..Default::default()
16563    };
16564    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16565
16566    cx.set_state(indoc! {"
16567        struct Fˇoo {}
16568    "});
16569
16570    cx.update_editor(|editor, _window, cx| {
16571        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16572        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16573        editor.highlight_background::<DocumentHighlightRead>(
16574            &[highlight_range],
16575            |c| c.editor_document_highlight_read_background,
16576            cx,
16577        );
16578    });
16579
16580    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16581        .expect("Prepare rename was not started")
16582        .await
16583        .expect("Prepare rename failed");
16584
16585    let mut rename_handler =
16586        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16587            let edit = lsp::TextEdit {
16588                range: lsp::Range {
16589                    start: lsp::Position {
16590                        line: 0,
16591                        character: 7,
16592                    },
16593                    end: lsp::Position {
16594                        line: 0,
16595                        character: 10,
16596                    },
16597                },
16598                new_text: "FooRenamed".to_string(),
16599            };
16600            Ok(Some(lsp::WorkspaceEdit::new(
16601                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16602            )))
16603        });
16604    let rename_task = cx
16605        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16606        .expect("Confirm rename was not started");
16607    rename_handler.next().await.unwrap();
16608    rename_task.await.expect("Confirm rename failed");
16609    cx.run_until_parked();
16610
16611    // Correct range is renamed, as `surrounding_word` is used to find it.
16612    cx.assert_editor_state(indoc! {"
16613        struct FooRenamedˇ {}
16614    "});
16615}
16616
16617#[gpui::test]
16618async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16619    init_test(cx, |_| {});
16620    let mut cx = EditorTestContext::new(cx).await;
16621
16622    let language = Arc::new(
16623        Language::new(
16624            LanguageConfig::default(),
16625            Some(tree_sitter_html::LANGUAGE.into()),
16626        )
16627        .with_brackets_query(
16628            r#"
16629            ("<" @open "/>" @close)
16630            ("</" @open ">" @close)
16631            ("<" @open ">" @close)
16632            ("\"" @open "\"" @close)
16633            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16634        "#,
16635        )
16636        .unwrap(),
16637    );
16638    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16639
16640    cx.set_state(indoc! {"
16641        <span>ˇ</span>
16642    "});
16643    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16644    cx.assert_editor_state(indoc! {"
16645        <span>
16646        ˇ
16647        </span>
16648    "});
16649
16650    cx.set_state(indoc! {"
16651        <span><span></span>ˇ</span>
16652    "});
16653    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16654    cx.assert_editor_state(indoc! {"
16655        <span><span></span>
16656        ˇ</span>
16657    "});
16658
16659    cx.set_state(indoc! {"
16660        <span>ˇ
16661        </span>
16662    "});
16663    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16664    cx.assert_editor_state(indoc! {"
16665        <span>
16666        ˇ
16667        </span>
16668    "});
16669}
16670
16671fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16672    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16673    point..point
16674}
16675
16676fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16677    let (text, ranges) = marked_text_ranges(marked_text, true);
16678    assert_eq!(editor.text(cx), text);
16679    assert_eq!(
16680        editor.selections.ranges(cx),
16681        ranges,
16682        "Assert selections are {}",
16683        marked_text
16684    );
16685}
16686
16687pub fn handle_signature_help_request(
16688    cx: &mut EditorLspTestContext,
16689    mocked_response: lsp::SignatureHelp,
16690) -> impl Future<Output = ()> {
16691    let mut request =
16692        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16693            let mocked_response = mocked_response.clone();
16694            async move { Ok(Some(mocked_response)) }
16695        });
16696
16697    async move {
16698        request.next().await;
16699    }
16700}
16701
16702/// Handle completion request passing a marked string specifying where the completion
16703/// should be triggered from using '|' character, what range should be replaced, and what completions
16704/// should be returned using '<' and '>' to delimit the range
16705pub fn handle_completion_request(
16706    cx: &mut EditorLspTestContext,
16707    marked_string: &str,
16708    completions: Vec<&'static str>,
16709    counter: Arc<AtomicUsize>,
16710) -> impl Future<Output = ()> {
16711    let complete_from_marker: TextRangeMarker = '|'.into();
16712    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16713    let (_, mut marked_ranges) = marked_text_ranges_by(
16714        marked_string,
16715        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16716    );
16717
16718    let complete_from_position =
16719        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16720    let replace_range =
16721        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16722
16723    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16724        let completions = completions.clone();
16725        counter.fetch_add(1, atomic::Ordering::Release);
16726        async move {
16727            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16728            assert_eq!(
16729                params.text_document_position.position,
16730                complete_from_position
16731            );
16732            Ok(Some(lsp::CompletionResponse::Array(
16733                completions
16734                    .iter()
16735                    .map(|completion_text| lsp::CompletionItem {
16736                        label: completion_text.to_string(),
16737                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16738                            range: replace_range,
16739                            new_text: completion_text.to_string(),
16740                        })),
16741                        ..Default::default()
16742                    })
16743                    .collect(),
16744            )))
16745        }
16746    });
16747
16748    async move {
16749        request.next().await;
16750    }
16751}
16752
16753fn handle_resolve_completion_request(
16754    cx: &mut EditorLspTestContext,
16755    edits: Option<Vec<(&'static str, &'static str)>>,
16756) -> impl Future<Output = ()> {
16757    let edits = edits.map(|edits| {
16758        edits
16759            .iter()
16760            .map(|(marked_string, new_text)| {
16761                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16762                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16763                lsp::TextEdit::new(replace_range, new_text.to_string())
16764            })
16765            .collect::<Vec<_>>()
16766    });
16767
16768    let mut request =
16769        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16770            let edits = edits.clone();
16771            async move {
16772                Ok(lsp::CompletionItem {
16773                    additional_text_edits: edits,
16774                    ..Default::default()
16775                })
16776            }
16777        });
16778
16779    async move {
16780        request.next().await;
16781    }
16782}
16783
16784pub(crate) fn update_test_language_settings(
16785    cx: &mut TestAppContext,
16786    f: impl Fn(&mut AllLanguageSettingsContent),
16787) {
16788    cx.update(|cx| {
16789        SettingsStore::update_global(cx, |store, cx| {
16790            store.update_user_settings::<AllLanguageSettings>(cx, f);
16791        });
16792    });
16793}
16794
16795pub(crate) fn update_test_project_settings(
16796    cx: &mut TestAppContext,
16797    f: impl Fn(&mut ProjectSettings),
16798) {
16799    cx.update(|cx| {
16800        SettingsStore::update_global(cx, |store, cx| {
16801            store.update_user_settings::<ProjectSettings>(cx, f);
16802        });
16803    });
16804}
16805
16806pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16807    cx.update(|cx| {
16808        assets::Assets.load_test_fonts(cx);
16809        let store = SettingsStore::test(cx);
16810        cx.set_global(store);
16811        theme::init(theme::LoadThemes::JustBase, cx);
16812        release_channel::init(SemanticVersion::default(), cx);
16813        client::init_settings(cx);
16814        language::init(cx);
16815        Project::init_settings(cx);
16816        workspace::init_settings(cx);
16817        crate::init(cx);
16818    });
16819
16820    update_test_language_settings(cx, f);
16821}
16822
16823#[track_caller]
16824fn assert_hunk_revert(
16825    not_reverted_text_with_selections: &str,
16826    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
16827    expected_reverted_text_with_selections: &str,
16828    base_text: &str,
16829    cx: &mut EditorLspTestContext,
16830) {
16831    cx.set_state(not_reverted_text_with_selections);
16832    cx.set_head_text(base_text);
16833    cx.executor().run_until_parked();
16834
16835    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16836        let snapshot = editor.snapshot(window, cx);
16837        let reverted_hunk_statuses = snapshot
16838            .buffer_snapshot
16839            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16840            .map(|hunk| hunk.status().kind)
16841            .collect::<Vec<_>>();
16842
16843        editor.git_restore(&Default::default(), window, cx);
16844        reverted_hunk_statuses
16845    });
16846    cx.executor().run_until_parked();
16847    cx.assert_editor_state(expected_reverted_text_with_selections);
16848    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16849}