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 futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::IndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   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 { level: 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 { level: 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 { level: 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    };
 1515
 1516    let move_to_end = MoveToEndOfLine {
 1517        stop_at_soft_wraps: true,
 1518    };
 1519
 1520    let editor = cx.add_window(|window, cx| {
 1521        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1522        build_editor(buffer, window, cx)
 1523    });
 1524    _ = editor.update(cx, |editor, window, cx| {
 1525        editor.change_selections(None, window, cx, |s| {
 1526            s.select_display_ranges([
 1527                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1528                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1529            ]);
 1530        });
 1531    });
 1532
 1533    _ = editor.update(cx, |editor, window, cx| {
 1534        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1535        assert_eq!(
 1536            editor.selections.display_ranges(cx),
 1537            &[
 1538                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1539                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1540            ]
 1541        );
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_end_of_line(&move_to_end, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1572                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1573            ]
 1574        );
 1575    });
 1576
 1577    // Moving to the end of line again is a no-op.
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_left(&MoveLeft, window, cx);
 1591        editor.select_to_beginning_of_line(
 1592            &SelectToBeginningOfLine {
 1593                stop_at_soft_wraps: true,
 1594            },
 1595            window,
 1596            cx,
 1597        );
 1598        assert_eq!(
 1599            editor.selections.display_ranges(cx),
 1600            &[
 1601                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1602                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1603            ]
 1604        );
 1605    });
 1606
 1607    _ = editor.update(cx, |editor, window, cx| {
 1608        editor.select_to_beginning_of_line(
 1609            &SelectToBeginningOfLine {
 1610                stop_at_soft_wraps: true,
 1611            },
 1612            window,
 1613            cx,
 1614        );
 1615        assert_eq!(
 1616            editor.selections.display_ranges(cx),
 1617            &[
 1618                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1619                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1620            ]
 1621        );
 1622    });
 1623
 1624    _ = editor.update(cx, |editor, window, cx| {
 1625        editor.select_to_beginning_of_line(
 1626            &SelectToBeginningOfLine {
 1627                stop_at_soft_wraps: true,
 1628            },
 1629            window,
 1630            cx,
 1631        );
 1632        assert_eq!(
 1633            editor.selections.display_ranges(cx),
 1634            &[
 1635                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1636                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1637            ]
 1638        );
 1639    });
 1640
 1641    _ = editor.update(cx, |editor, window, cx| {
 1642        editor.select_to_end_of_line(
 1643            &SelectToEndOfLine {
 1644                stop_at_soft_wraps: true,
 1645            },
 1646            window,
 1647            cx,
 1648        );
 1649        assert_eq!(
 1650            editor.selections.display_ranges(cx),
 1651            &[
 1652                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1653                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1654            ]
 1655        );
 1656    });
 1657
 1658    _ = editor.update(cx, |editor, window, cx| {
 1659        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1660        assert_eq!(editor.display_text(cx), "ab\n  de");
 1661        assert_eq!(
 1662            editor.selections.display_ranges(cx),
 1663            &[
 1664                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1665                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1666            ]
 1667        );
 1668    });
 1669
 1670    _ = editor.update(cx, |editor, window, cx| {
 1671        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1672        assert_eq!(editor.display_text(cx), "\n");
 1673        assert_eq!(
 1674            editor.selections.display_ranges(cx),
 1675            &[
 1676                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1677                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1678            ]
 1679        );
 1680    });
 1681}
 1682
 1683#[gpui::test]
 1684fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1685    init_test(cx, |_| {});
 1686    let move_to_beg = MoveToBeginningOfLine {
 1687        stop_at_soft_wraps: false,
 1688    };
 1689
 1690    let move_to_end = MoveToEndOfLine {
 1691        stop_at_soft_wraps: false,
 1692    };
 1693
 1694    let editor = cx.add_window(|window, cx| {
 1695        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1696        build_editor(buffer, window, cx)
 1697    });
 1698
 1699    _ = editor.update(cx, |editor, window, cx| {
 1700        editor.set_wrap_width(Some(140.0.into()), cx);
 1701
 1702        // We expect the following lines after wrapping
 1703        // ```
 1704        // thequickbrownfox
 1705        // jumpedoverthelazydo
 1706        // gs
 1707        // ```
 1708        // The final `gs` was soft-wrapped onto a new line.
 1709        assert_eq!(
 1710            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1711            editor.display_text(cx),
 1712        );
 1713
 1714        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1715        // Start the cursor at the `k` on the first line
 1716        editor.change_selections(None, window, cx, |s| {
 1717            s.select_display_ranges([
 1718                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1719            ]);
 1720        });
 1721
 1722        // Moving to the beginning of the line should put us at the beginning of the line.
 1723        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1724        assert_eq!(
 1725            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1726            editor.selections.display_ranges(cx)
 1727        );
 1728
 1729        // Moving to the end of the line should put us at the end of the line.
 1730        editor.move_to_end_of_line(&move_to_end, window, cx);
 1731        assert_eq!(
 1732            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1733            editor.selections.display_ranges(cx)
 1734        );
 1735
 1736        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1737        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1738        editor.change_selections(None, window, cx, |s| {
 1739            s.select_display_ranges([
 1740                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1741            ]);
 1742        });
 1743
 1744        // Moving to the beginning of the line should put us at the start of the second line of
 1745        // display text, i.e., the `j`.
 1746        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Moving to the beginning of the line again should be a no-op.
 1753        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1754        assert_eq!(
 1755            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1756            editor.selections.display_ranges(cx)
 1757        );
 1758
 1759        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1760        // next display line.
 1761        editor.move_to_end_of_line(&move_to_end, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the end of the line again should be a no-op.
 1768        editor.move_to_end_of_line(&move_to_end, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773    });
 1774}
 1775
 1776#[gpui::test]
 1777fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1778    init_test(cx, |_| {});
 1779
 1780    let editor = cx.add_window(|window, cx| {
 1781        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1782        build_editor(buffer, window, cx)
 1783    });
 1784    _ = editor.update(cx, |editor, window, cx| {
 1785        editor.change_selections(None, window, cx, |s| {
 1786            s.select_display_ranges([
 1787                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1788                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1789            ])
 1790        });
 1791
 1792        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1793        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1794
 1795        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1796        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1797
 1798        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1799        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1800
 1801        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1802        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1803
 1804        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1805        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1806
 1807        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1808        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1809
 1810        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1811        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1812
 1813        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1814        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1815
 1816        editor.move_right(&MoveRight, window, cx);
 1817        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1818        assert_selection_ranges(
 1819            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1820            editor,
 1821            cx,
 1822        );
 1823
 1824        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1825        assert_selection_ranges(
 1826            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1827            editor,
 1828            cx,
 1829        );
 1830
 1831        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1832        assert_selection_ranges(
 1833            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1834            editor,
 1835            cx,
 1836        );
 1837    });
 1838}
 1839
 1840#[gpui::test]
 1841fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1842    init_test(cx, |_| {});
 1843
 1844    let editor = cx.add_window(|window, cx| {
 1845        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1846        build_editor(buffer, window, cx)
 1847    });
 1848
 1849    _ = editor.update(cx, |editor, window, cx| {
 1850        editor.set_wrap_width(Some(140.0.into()), cx);
 1851        assert_eq!(
 1852            editor.display_text(cx),
 1853            "use one::{\n    two::three::\n    four::five\n};"
 1854        );
 1855
 1856        editor.change_selections(None, window, cx, |s| {
 1857            s.select_display_ranges([
 1858                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1859            ]);
 1860        });
 1861
 1862        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1866        );
 1867
 1868        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1869        assert_eq!(
 1870            editor.selections.display_ranges(cx),
 1871            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1872        );
 1873
 1874        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1878        );
 1879
 1880        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1881        assert_eq!(
 1882            editor.selections.display_ranges(cx),
 1883            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1884        );
 1885
 1886        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1887        assert_eq!(
 1888            editor.selections.display_ranges(cx),
 1889            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1890        );
 1891
 1892        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1893        assert_eq!(
 1894            editor.selections.display_ranges(cx),
 1895            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1896        );
 1897    });
 1898}
 1899
 1900#[gpui::test]
 1901async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1902    init_test(cx, |_| {});
 1903    let mut cx = EditorTestContext::new(cx).await;
 1904
 1905    let line_height = cx.editor(|editor, window, _| {
 1906        editor
 1907            .style()
 1908            .unwrap()
 1909            .text
 1910            .line_height_in_pixels(window.rem_size())
 1911    });
 1912    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1913
 1914    cx.set_state(
 1915        &r#"ˇone
 1916        two
 1917
 1918        three
 1919        fourˇ
 1920        five
 1921
 1922        six"#
 1923            .unindent(),
 1924    );
 1925
 1926    cx.update_editor(|editor, window, cx| {
 1927        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1928    });
 1929    cx.assert_editor_state(
 1930        &r#"one
 1931        two
 1932        ˇ
 1933        three
 1934        four
 1935        five
 1936        ˇ
 1937        six"#
 1938            .unindent(),
 1939    );
 1940
 1941    cx.update_editor(|editor, window, cx| {
 1942        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1943    });
 1944    cx.assert_editor_state(
 1945        &r#"one
 1946        two
 1947
 1948        three
 1949        four
 1950        five
 1951        ˇ
 1952        sixˇ"#
 1953            .unindent(),
 1954    );
 1955
 1956    cx.update_editor(|editor, window, cx| {
 1957        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1958    });
 1959    cx.assert_editor_state(
 1960        &r#"one
 1961        two
 1962
 1963        three
 1964        four
 1965        five
 1966
 1967        sixˇ"#
 1968            .unindent(),
 1969    );
 1970
 1971    cx.update_editor(|editor, window, cx| {
 1972        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1973    });
 1974    cx.assert_editor_state(
 1975        &r#"one
 1976        two
 1977
 1978        three
 1979        four
 1980        five
 1981        ˇ
 1982        six"#
 1983            .unindent(),
 1984    );
 1985
 1986    cx.update_editor(|editor, window, cx| {
 1987        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1988    });
 1989    cx.assert_editor_state(
 1990        &r#"one
 1991        two
 1992        ˇ
 1993        three
 1994        four
 1995        five
 1996
 1997        six"#
 1998            .unindent(),
 1999    );
 2000
 2001    cx.update_editor(|editor, window, cx| {
 2002        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2003    });
 2004    cx.assert_editor_state(
 2005        &r#"ˇone
 2006        two
 2007
 2008        three
 2009        four
 2010        five
 2011
 2012        six"#
 2013            .unindent(),
 2014    );
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    let window = cx.window;
 2029    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2030
 2031    cx.set_state(
 2032        r#"ˇone
 2033        two
 2034        three
 2035        four
 2036        five
 2037        six
 2038        seven
 2039        eight
 2040        nine
 2041        ten
 2042        "#,
 2043    );
 2044
 2045    cx.update_editor(|editor, window, cx| {
 2046        assert_eq!(
 2047            editor.snapshot(window, cx).scroll_position(),
 2048            gpui::Point::new(0., 0.)
 2049        );
 2050        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2051        assert_eq!(
 2052            editor.snapshot(window, cx).scroll_position(),
 2053            gpui::Point::new(0., 3.)
 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., 6.)
 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., 3.)
 2064        );
 2065
 2066        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2067        assert_eq!(
 2068            editor.snapshot(window, cx).scroll_position(),
 2069            gpui::Point::new(0., 1.)
 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., 3.)
 2075        );
 2076    });
 2077}
 2078
 2079#[gpui::test]
 2080async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2081    init_test(cx, |_| {});
 2082    let mut cx = EditorTestContext::new(cx).await;
 2083
 2084    let line_height = cx.update_editor(|editor, window, cx| {
 2085        editor.set_vertical_scroll_margin(2, cx);
 2086        editor
 2087            .style()
 2088            .unwrap()
 2089            .text
 2090            .line_height_in_pixels(window.rem_size())
 2091    });
 2092    let window = cx.window;
 2093    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2094
 2095    cx.set_state(
 2096        r#"ˇone
 2097            two
 2098            three
 2099            four
 2100            five
 2101            six
 2102            seven
 2103            eight
 2104            nine
 2105            ten
 2106        "#,
 2107    );
 2108    cx.update_editor(|editor, window, cx| {
 2109        assert_eq!(
 2110            editor.snapshot(window, cx).scroll_position(),
 2111            gpui::Point::new(0., 0.0)
 2112        );
 2113    });
 2114
 2115    // Add a cursor below the visible area. Since both cursors cannot fit
 2116    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2117    // allows the vertical scroll margin below that cursor.
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2120            selections.select_ranges([
 2121                Point::new(0, 0)..Point::new(0, 0),
 2122                Point::new(6, 0)..Point::new(6, 0),
 2123            ]);
 2124        })
 2125    });
 2126    cx.update_editor(|editor, window, cx| {
 2127        assert_eq!(
 2128            editor.snapshot(window, cx).scroll_position(),
 2129            gpui::Point::new(0., 3.0)
 2130        );
 2131    });
 2132
 2133    // Move down. The editor cursor scrolls down to track the newest cursor.
 2134    cx.update_editor(|editor, window, cx| {
 2135        editor.move_down(&Default::default(), window, cx);
 2136    });
 2137    cx.update_editor(|editor, window, cx| {
 2138        assert_eq!(
 2139            editor.snapshot(window, cx).scroll_position(),
 2140            gpui::Point::new(0., 4.0)
 2141        );
 2142    });
 2143
 2144    // Add a cursor above the visible area. Since both cursors fit on screen,
 2145    // the editor scrolls to show both.
 2146    cx.update_editor(|editor, window, cx| {
 2147        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2148            selections.select_ranges([
 2149                Point::new(1, 0)..Point::new(1, 0),
 2150                Point::new(6, 0)..Point::new(6, 0),
 2151            ]);
 2152        })
 2153    });
 2154    cx.update_editor(|editor, window, cx| {
 2155        assert_eq!(
 2156            editor.snapshot(window, cx).scroll_position(),
 2157            gpui::Point::new(0., 1.0)
 2158        );
 2159    });
 2160}
 2161
 2162#[gpui::test]
 2163async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2164    init_test(cx, |_| {});
 2165    let mut cx = EditorTestContext::new(cx).await;
 2166
 2167    let line_height = cx.editor(|editor, window, _cx| {
 2168        editor
 2169            .style()
 2170            .unwrap()
 2171            .text
 2172            .line_height_in_pixels(window.rem_size())
 2173    });
 2174    let window = cx.window;
 2175    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2176    cx.set_state(
 2177        &r#"
 2178        ˇone
 2179        two
 2180        threeˇ
 2181        four
 2182        five
 2183        six
 2184        seven
 2185        eight
 2186        nine
 2187        ten
 2188        "#
 2189        .unindent(),
 2190    );
 2191
 2192    cx.update_editor(|editor, window, cx| {
 2193        editor.move_page_down(&MovePageDown::default(), window, cx)
 2194    });
 2195    cx.assert_editor_state(
 2196        &r#"
 2197        one
 2198        two
 2199        three
 2200        ˇfour
 2201        five
 2202        sixˇ
 2203        seven
 2204        eight
 2205        nine
 2206        ten
 2207        "#
 2208        .unindent(),
 2209    );
 2210
 2211    cx.update_editor(|editor, window, cx| {
 2212        editor.move_page_down(&MovePageDown::default(), window, cx)
 2213    });
 2214    cx.assert_editor_state(
 2215        &r#"
 2216        one
 2217        two
 2218        three
 2219        four
 2220        five
 2221        six
 2222        ˇseven
 2223        eight
 2224        nineˇ
 2225        ten
 2226        "#
 2227        .unindent(),
 2228    );
 2229
 2230    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2231    cx.assert_editor_state(
 2232        &r#"
 2233        one
 2234        two
 2235        three
 2236        ˇfour
 2237        five
 2238        sixˇ
 2239        seven
 2240        eight
 2241        nine
 2242        ten
 2243        "#
 2244        .unindent(),
 2245    );
 2246
 2247    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2248    cx.assert_editor_state(
 2249        &r#"
 2250        ˇone
 2251        two
 2252        threeˇ
 2253        four
 2254        five
 2255        six
 2256        seven
 2257        eight
 2258        nine
 2259        ten
 2260        "#
 2261        .unindent(),
 2262    );
 2263
 2264    // Test select collapsing
 2265    cx.update_editor(|editor, window, cx| {
 2266        editor.move_page_down(&MovePageDown::default(), window, cx);
 2267        editor.move_page_down(&MovePageDown::default(), window, cx);
 2268        editor.move_page_down(&MovePageDown::default(), window, cx);
 2269    });
 2270    cx.assert_editor_state(
 2271        &r#"
 2272        one
 2273        two
 2274        three
 2275        four
 2276        five
 2277        six
 2278        seven
 2279        eight
 2280        nine
 2281        ˇten
 2282        ˇ"#
 2283        .unindent(),
 2284    );
 2285}
 2286
 2287#[gpui::test]
 2288async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2289    init_test(cx, |_| {});
 2290    let mut cx = EditorTestContext::new(cx).await;
 2291    cx.set_state("one «two threeˇ» four");
 2292    cx.update_editor(|editor, window, cx| {
 2293        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2294        assert_eq!(editor.text(cx), " four");
 2295    });
 2296}
 2297
 2298#[gpui::test]
 2299fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2300    init_test(cx, |_| {});
 2301
 2302    let editor = cx.add_window(|window, cx| {
 2303        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2304        build_editor(buffer.clone(), window, cx)
 2305    });
 2306
 2307    _ = editor.update(cx, |editor, window, cx| {
 2308        editor.change_selections(None, window, cx, |s| {
 2309            s.select_display_ranges([
 2310                // an empty selection - the preceding word fragment is deleted
 2311                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2312                // characters selected - they are deleted
 2313                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2314            ])
 2315        });
 2316        editor.delete_to_previous_word_start(
 2317            &DeleteToPreviousWordStart {
 2318                ignore_newlines: false,
 2319            },
 2320            window,
 2321            cx,
 2322        );
 2323        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2324    });
 2325
 2326    _ = editor.update(cx, |editor, window, cx| {
 2327        editor.change_selections(None, window, cx, |s| {
 2328            s.select_display_ranges([
 2329                // an empty selection - the following word fragment is deleted
 2330                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2331                // characters selected - they are deleted
 2332                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2333            ])
 2334        });
 2335        editor.delete_to_next_word_end(
 2336            &DeleteToNextWordEnd {
 2337                ignore_newlines: false,
 2338            },
 2339            window,
 2340            cx,
 2341        );
 2342        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2343    });
 2344}
 2345
 2346#[gpui::test]
 2347fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2348    init_test(cx, |_| {});
 2349
 2350    let editor = cx.add_window(|window, cx| {
 2351        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2352        build_editor(buffer.clone(), window, cx)
 2353    });
 2354    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2355        ignore_newlines: false,
 2356    };
 2357    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2358        ignore_newlines: true,
 2359    };
 2360
 2361    _ = editor.update(cx, |editor, window, cx| {
 2362        editor.change_selections(None, window, cx, |s| {
 2363            s.select_display_ranges([
 2364                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2365            ])
 2366        });
 2367        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2368        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2369        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2370        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2371        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2372        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2373        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2374        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2375        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2376        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2377        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2378        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2379    });
 2380}
 2381
 2382#[gpui::test]
 2383fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2384    init_test(cx, |_| {});
 2385
 2386    let editor = cx.add_window(|window, cx| {
 2387        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2388        build_editor(buffer.clone(), window, cx)
 2389    });
 2390    let del_to_next_word_end = DeleteToNextWordEnd {
 2391        ignore_newlines: false,
 2392    };
 2393    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2394        ignore_newlines: true,
 2395    };
 2396
 2397    _ = editor.update(cx, |editor, window, cx| {
 2398        editor.change_selections(None, window, cx, |s| {
 2399            s.select_display_ranges([
 2400                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2401            ])
 2402        });
 2403        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2404        assert_eq!(
 2405            editor.buffer.read(cx).read(cx).text(),
 2406            "one\n   two\nthree\n   four"
 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            "\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            "two\nthree\n   four"
 2417        );
 2418        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2419        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2420        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2421        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2422        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2423        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2424    });
 2425}
 2426
 2427#[gpui::test]
 2428fn test_newline(cx: &mut TestAppContext) {
 2429    init_test(cx, |_| {});
 2430
 2431    let editor = cx.add_window(|window, cx| {
 2432        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2433        build_editor(buffer.clone(), window, cx)
 2434    });
 2435
 2436    _ = editor.update(cx, |editor, window, cx| {
 2437        editor.change_selections(None, window, cx, |s| {
 2438            s.select_display_ranges([
 2439                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2440                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2441                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2442            ])
 2443        });
 2444
 2445        editor.newline(&Newline, window, cx);
 2446        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2447    });
 2448}
 2449
 2450#[gpui::test]
 2451fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2452    init_test(cx, |_| {});
 2453
 2454    let editor = cx.add_window(|window, cx| {
 2455        let buffer = MultiBuffer::build_simple(
 2456            "
 2457                a
 2458                b(
 2459                    X
 2460                )
 2461                c(
 2462                    X
 2463                )
 2464            "
 2465            .unindent()
 2466            .as_str(),
 2467            cx,
 2468        );
 2469        let mut editor = build_editor(buffer.clone(), window, cx);
 2470        editor.change_selections(None, window, cx, |s| {
 2471            s.select_ranges([
 2472                Point::new(2, 4)..Point::new(2, 5),
 2473                Point::new(5, 4)..Point::new(5, 5),
 2474            ])
 2475        });
 2476        editor
 2477    });
 2478
 2479    _ = editor.update(cx, |editor, window, cx| {
 2480        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2481        editor.buffer.update(cx, |buffer, cx| {
 2482            buffer.edit(
 2483                [
 2484                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2485                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2486                ],
 2487                None,
 2488                cx,
 2489            );
 2490            assert_eq!(
 2491                buffer.read(cx).text(),
 2492                "
 2493                    a
 2494                    b()
 2495                    c()
 2496                "
 2497                .unindent()
 2498            );
 2499        });
 2500        assert_eq!(
 2501            editor.selections.ranges(cx),
 2502            &[
 2503                Point::new(1, 2)..Point::new(1, 2),
 2504                Point::new(2, 2)..Point::new(2, 2),
 2505            ],
 2506        );
 2507
 2508        editor.newline(&Newline, window, cx);
 2509        assert_eq!(
 2510            editor.text(cx),
 2511            "
 2512                a
 2513                b(
 2514                )
 2515                c(
 2516                )
 2517            "
 2518            .unindent()
 2519        );
 2520
 2521        // The selections are moved after the inserted newlines
 2522        assert_eq!(
 2523            editor.selections.ranges(cx),
 2524            &[
 2525                Point::new(2, 0)..Point::new(2, 0),
 2526                Point::new(4, 0)..Point::new(4, 0),
 2527            ],
 2528        );
 2529    });
 2530}
 2531
 2532#[gpui::test]
 2533async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2534    init_test(cx, |settings| {
 2535        settings.defaults.tab_size = NonZeroU32::new(4)
 2536    });
 2537
 2538    let language = Arc::new(
 2539        Language::new(
 2540            LanguageConfig::default(),
 2541            Some(tree_sitter_rust::LANGUAGE.into()),
 2542        )
 2543        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2544        .unwrap(),
 2545    );
 2546
 2547    let mut cx = EditorTestContext::new(cx).await;
 2548    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2549    cx.set_state(indoc! {"
 2550        const a: ˇA = (
 2551 2552                «const_functionˇ»(ˇ),
 2553                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2554 2555        ˇ);ˇ
 2556    "});
 2557
 2558    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2559    cx.assert_editor_state(indoc! {"
 2560        ˇ
 2561        const a: A = (
 2562            ˇ
 2563            (
 2564                ˇ
 2565                ˇ
 2566                const_function(),
 2567                ˇ
 2568                ˇ
 2569                ˇ
 2570                ˇ
 2571                something_else,
 2572                ˇ
 2573            )
 2574            ˇ
 2575            ˇ
 2576        );
 2577    "});
 2578}
 2579
 2580#[gpui::test]
 2581async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2582    init_test(cx, |settings| {
 2583        settings.defaults.tab_size = NonZeroU32::new(4)
 2584    });
 2585
 2586    let language = Arc::new(
 2587        Language::new(
 2588            LanguageConfig::default(),
 2589            Some(tree_sitter_rust::LANGUAGE.into()),
 2590        )
 2591        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2592        .unwrap(),
 2593    );
 2594
 2595    let mut cx = EditorTestContext::new(cx).await;
 2596    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2597    cx.set_state(indoc! {"
 2598        const a: ˇA = (
 2599 2600                «const_functionˇ»(ˇ),
 2601                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2602 2603        ˇ);ˇ
 2604    "});
 2605
 2606    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2607    cx.assert_editor_state(indoc! {"
 2608        const a: A = (
 2609            ˇ
 2610            (
 2611                ˇ
 2612                const_function(),
 2613                ˇ
 2614                ˇ
 2615                something_else,
 2616                ˇ
 2617                ˇ
 2618                ˇ
 2619                ˇ
 2620            )
 2621            ˇ
 2622        );
 2623        ˇ
 2624        ˇ
 2625    "});
 2626}
 2627
 2628#[gpui::test]
 2629async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2630    init_test(cx, |settings| {
 2631        settings.defaults.tab_size = NonZeroU32::new(4)
 2632    });
 2633
 2634    let language = Arc::new(Language::new(
 2635        LanguageConfig {
 2636            line_comments: vec!["//".into()],
 2637            ..LanguageConfig::default()
 2638        },
 2639        None,
 2640    ));
 2641    {
 2642        let mut cx = EditorTestContext::new(cx).await;
 2643        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2644        cx.set_state(indoc! {"
 2645        // Fooˇ
 2646    "});
 2647
 2648        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2649        cx.assert_editor_state(indoc! {"
 2650        // Foo
 2651        //ˇ
 2652    "});
 2653        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2654        cx.set_state(indoc! {"
 2655        ˇ// Foo
 2656    "});
 2657        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2658        cx.assert_editor_state(indoc! {"
 2659
 2660        ˇ// Foo
 2661    "});
 2662    }
 2663    // Ensure that comment continuations can be disabled.
 2664    update_test_language_settings(cx, |settings| {
 2665        settings.defaults.extend_comment_on_newline = Some(false);
 2666    });
 2667    let mut cx = EditorTestContext::new(cx).await;
 2668    cx.set_state(indoc! {"
 2669        // Fooˇ
 2670    "});
 2671    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2672    cx.assert_editor_state(indoc! {"
 2673        // Foo
 2674        ˇ
 2675    "});
 2676}
 2677
 2678#[gpui::test]
 2679fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2680    init_test(cx, |_| {});
 2681
 2682    let editor = cx.add_window(|window, cx| {
 2683        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2684        let mut editor = build_editor(buffer.clone(), window, cx);
 2685        editor.change_selections(None, window, cx, |s| {
 2686            s.select_ranges([3..4, 11..12, 19..20])
 2687        });
 2688        editor
 2689    });
 2690
 2691    _ = editor.update(cx, |editor, window, cx| {
 2692        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2693        editor.buffer.update(cx, |buffer, cx| {
 2694            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2695            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2696        });
 2697        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2698
 2699        editor.insert("Z", window, cx);
 2700        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2701
 2702        // The selections are moved after the inserted characters
 2703        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2704    });
 2705}
 2706
 2707#[gpui::test]
 2708async fn test_tab(cx: &mut gpui::TestAppContext) {
 2709    init_test(cx, |settings| {
 2710        settings.defaults.tab_size = NonZeroU32::new(3)
 2711    });
 2712
 2713    let mut cx = EditorTestContext::new(cx).await;
 2714    cx.set_state(indoc! {"
 2715        ˇabˇc
 2716        ˇ🏀ˇ🏀ˇefg
 2717 2718    "});
 2719    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2720    cx.assert_editor_state(indoc! {"
 2721           ˇab ˇc
 2722           ˇ🏀  ˇ🏀  ˇefg
 2723        d  ˇ
 2724    "});
 2725
 2726    cx.set_state(indoc! {"
 2727        a
 2728        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2729    "});
 2730    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2731    cx.assert_editor_state(indoc! {"
 2732        a
 2733           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2734    "});
 2735}
 2736
 2737#[gpui::test]
 2738async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2739    init_test(cx, |_| {});
 2740
 2741    let mut cx = EditorTestContext::new(cx).await;
 2742    let language = Arc::new(
 2743        Language::new(
 2744            LanguageConfig::default(),
 2745            Some(tree_sitter_rust::LANGUAGE.into()),
 2746        )
 2747        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2748        .unwrap(),
 2749    );
 2750    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2751
 2752    // cursors that are already at the suggested indent level insert
 2753    // a soft tab. cursors that are to the left of the suggested indent
 2754    // auto-indent their line.
 2755    cx.set_state(indoc! {"
 2756        ˇ
 2757        const a: B = (
 2758            c(
 2759                d(
 2760        ˇ
 2761                )
 2762        ˇ
 2763        ˇ    )
 2764        );
 2765    "});
 2766    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2767    cx.assert_editor_state(indoc! {"
 2768            ˇ
 2769        const a: B = (
 2770            c(
 2771                d(
 2772                    ˇ
 2773                )
 2774                ˇ
 2775            ˇ)
 2776        );
 2777    "});
 2778
 2779    // handle auto-indent when there are multiple cursors on the same line
 2780    cx.set_state(indoc! {"
 2781        const a: B = (
 2782            c(
 2783        ˇ    ˇ
 2784        ˇ    )
 2785        );
 2786    "});
 2787    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2788    cx.assert_editor_state(indoc! {"
 2789        const a: B = (
 2790            c(
 2791                ˇ
 2792            ˇ)
 2793        );
 2794    "});
 2795}
 2796
 2797#[gpui::test]
 2798async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2799    init_test(cx, |settings| {
 2800        settings.defaults.tab_size = NonZeroU32::new(4)
 2801    });
 2802
 2803    let language = Arc::new(
 2804        Language::new(
 2805            LanguageConfig::default(),
 2806            Some(tree_sitter_rust::LANGUAGE.into()),
 2807        )
 2808        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2809        .unwrap(),
 2810    );
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2814    cx.set_state(indoc! {"
 2815        fn a() {
 2816            if b {
 2817        \t ˇc
 2818            }
 2819        }
 2820    "});
 2821
 2822    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2823    cx.assert_editor_state(indoc! {"
 2824        fn a() {
 2825            if b {
 2826                ˇc
 2827            }
 2828        }
 2829    "});
 2830}
 2831
 2832#[gpui::test]
 2833async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2834    init_test(cx, |settings| {
 2835        settings.defaults.tab_size = NonZeroU32::new(4);
 2836    });
 2837
 2838    let mut cx = EditorTestContext::new(cx).await;
 2839
 2840    cx.set_state(indoc! {"
 2841          «oneˇ» «twoˇ»
 2842        three
 2843         four
 2844    "});
 2845    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2846    cx.assert_editor_state(indoc! {"
 2847            «oneˇ» «twoˇ»
 2848        three
 2849         four
 2850    "});
 2851
 2852    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        «oneˇ» «twoˇ»
 2855        three
 2856         four
 2857    "});
 2858
 2859    // select across line ending
 2860    cx.set_state(indoc! {"
 2861        one two
 2862        t«hree
 2863        ˇ» four
 2864    "});
 2865    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2866    cx.assert_editor_state(indoc! {"
 2867        one two
 2868            t«hree
 2869        ˇ» four
 2870    "});
 2871
 2872    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2873    cx.assert_editor_state(indoc! {"
 2874        one two
 2875        t«hree
 2876        ˇ» four
 2877    "});
 2878
 2879    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2880    cx.set_state(indoc! {"
 2881        one two
 2882        ˇthree
 2883            four
 2884    "});
 2885    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2886    cx.assert_editor_state(indoc! {"
 2887        one two
 2888            ˇthree
 2889            four
 2890    "});
 2891
 2892    cx.set_state(indoc! {"
 2893        one two
 2894        ˇ    three
 2895            four
 2896    "});
 2897    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2898    cx.assert_editor_state(indoc! {"
 2899        one two
 2900        ˇthree
 2901            four
 2902    "});
 2903}
 2904
 2905#[gpui::test]
 2906async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2907    init_test(cx, |settings| {
 2908        settings.defaults.hard_tabs = Some(true);
 2909    });
 2910
 2911    let mut cx = EditorTestContext::new(cx).await;
 2912
 2913    // select two ranges on one line
 2914    cx.set_state(indoc! {"
 2915        «oneˇ» «twoˇ»
 2916        three
 2917        four
 2918    "});
 2919    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2920    cx.assert_editor_state(indoc! {"
 2921        \t«oneˇ» «twoˇ»
 2922        three
 2923        four
 2924    "});
 2925    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2926    cx.assert_editor_state(indoc! {"
 2927        \t\t«oneˇ» «twoˇ»
 2928        three
 2929        four
 2930    "});
 2931    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2932    cx.assert_editor_state(indoc! {"
 2933        \t«oneˇ» «twoˇ»
 2934        three
 2935        four
 2936    "});
 2937    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2938    cx.assert_editor_state(indoc! {"
 2939        «oneˇ» «twoˇ»
 2940        three
 2941        four
 2942    "});
 2943
 2944    // select across a line ending
 2945    cx.set_state(indoc! {"
 2946        one two
 2947        t«hree
 2948        ˇ»four
 2949    "});
 2950    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2951    cx.assert_editor_state(indoc! {"
 2952        one two
 2953        \tt«hree
 2954        ˇ»four
 2955    "});
 2956    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2957    cx.assert_editor_state(indoc! {"
 2958        one two
 2959        \t\tt«hree
 2960        ˇ»four
 2961    "});
 2962    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2963    cx.assert_editor_state(indoc! {"
 2964        one two
 2965        \tt«hree
 2966        ˇ»four
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        one two
 2971        t«hree
 2972        ˇ»four
 2973    "});
 2974
 2975    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2976    cx.set_state(indoc! {"
 2977        one two
 2978        ˇthree
 2979        four
 2980    "});
 2981    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2982    cx.assert_editor_state(indoc! {"
 2983        one two
 2984        ˇthree
 2985        four
 2986    "});
 2987    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2988    cx.assert_editor_state(indoc! {"
 2989        one two
 2990        \tˇthree
 2991        four
 2992    "});
 2993    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2994    cx.assert_editor_state(indoc! {"
 2995        one two
 2996        ˇthree
 2997        four
 2998    "});
 2999}
 3000
 3001#[gpui::test]
 3002fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3003    init_test(cx, |settings| {
 3004        settings.languages.extend([
 3005            (
 3006                "TOML".into(),
 3007                LanguageSettingsContent {
 3008                    tab_size: NonZeroU32::new(2),
 3009                    ..Default::default()
 3010                },
 3011            ),
 3012            (
 3013                "Rust".into(),
 3014                LanguageSettingsContent {
 3015                    tab_size: NonZeroU32::new(4),
 3016                    ..Default::default()
 3017                },
 3018            ),
 3019        ]);
 3020    });
 3021
 3022    let toml_language = Arc::new(Language::new(
 3023        LanguageConfig {
 3024            name: "TOML".into(),
 3025            ..Default::default()
 3026        },
 3027        None,
 3028    ));
 3029    let rust_language = Arc::new(Language::new(
 3030        LanguageConfig {
 3031            name: "Rust".into(),
 3032            ..Default::default()
 3033        },
 3034        None,
 3035    ));
 3036
 3037    let toml_buffer =
 3038        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3039    let rust_buffer =
 3040        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3041    let multibuffer = cx.new(|cx| {
 3042        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3043        multibuffer.push_excerpts(
 3044            toml_buffer.clone(),
 3045            [ExcerptRange {
 3046                context: Point::new(0, 0)..Point::new(2, 0),
 3047                primary: None,
 3048            }],
 3049            cx,
 3050        );
 3051        multibuffer.push_excerpts(
 3052            rust_buffer.clone(),
 3053            [ExcerptRange {
 3054                context: Point::new(0, 0)..Point::new(1, 0),
 3055                primary: None,
 3056            }],
 3057            cx,
 3058        );
 3059        multibuffer
 3060    });
 3061
 3062    cx.add_window(|window, cx| {
 3063        let mut editor = build_editor(multibuffer, window, cx);
 3064
 3065        assert_eq!(
 3066            editor.text(cx),
 3067            indoc! {"
 3068                a = 1
 3069                b = 2
 3070
 3071                const c: usize = 3;
 3072            "}
 3073        );
 3074
 3075        select_ranges(
 3076            &mut editor,
 3077            indoc! {"
 3078                «aˇ» = 1
 3079                b = 2
 3080
 3081                «const c:ˇ» usize = 3;
 3082            "},
 3083            window,
 3084            cx,
 3085        );
 3086
 3087        editor.tab(&Tab, window, cx);
 3088        assert_text_with_selections(
 3089            &mut editor,
 3090            indoc! {"
 3091                  «aˇ» = 1
 3092                b = 2
 3093
 3094                    «const c:ˇ» usize = 3;
 3095            "},
 3096            cx,
 3097        );
 3098        editor.tab_prev(&TabPrev, window, cx);
 3099        assert_text_with_selections(
 3100            &mut editor,
 3101            indoc! {"
 3102                «aˇ» = 1
 3103                b = 2
 3104
 3105                «const c:ˇ» usize = 3;
 3106            "},
 3107            cx,
 3108        );
 3109
 3110        editor
 3111    });
 3112}
 3113
 3114#[gpui::test]
 3115async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3116    init_test(cx, |_| {});
 3117
 3118    let mut cx = EditorTestContext::new(cx).await;
 3119
 3120    // Basic backspace
 3121    cx.set_state(indoc! {"
 3122        onˇe two three
 3123        fou«rˇ» five six
 3124        seven «ˇeight nine
 3125        »ten
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        oˇe two three
 3130        fouˇ five six
 3131        seven ˇten
 3132    "});
 3133
 3134    // Test backspace inside and around indents
 3135    cx.set_state(indoc! {"
 3136        zero
 3137            ˇone
 3138                ˇtwo
 3139            ˇ ˇ ˇ  three
 3140        ˇ  ˇ  four
 3141    "});
 3142    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3143    cx.assert_editor_state(indoc! {"
 3144        zero
 3145        ˇone
 3146            ˇtwo
 3147        ˇ  threeˇ  four
 3148    "});
 3149
 3150    // Test backspace with line_mode set to true
 3151    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3152    cx.set_state(indoc! {"
 3153        The ˇquick ˇbrown
 3154        fox jumps over
 3155        the lazy dog
 3156        ˇThe qu«ick bˇ»rown"});
 3157    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3158    cx.assert_editor_state(indoc! {"
 3159        ˇfox jumps over
 3160        the lazy dogˇ"});
 3161}
 3162
 3163#[gpui::test]
 3164async fn test_delete(cx: &mut gpui::TestAppContext) {
 3165    init_test(cx, |_| {});
 3166
 3167    let mut cx = EditorTestContext::new(cx).await;
 3168    cx.set_state(indoc! {"
 3169        onˇe two three
 3170        fou«rˇ» five six
 3171        seven «ˇeight nine
 3172        »ten
 3173    "});
 3174    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3175    cx.assert_editor_state(indoc! {"
 3176        onˇ two three
 3177        fouˇ five six
 3178        seven ˇten
 3179    "});
 3180
 3181    // Test backspace with line_mode set to true
 3182    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3183    cx.set_state(indoc! {"
 3184        The ˇquick ˇbrown
 3185        fox «ˇjum»ps over
 3186        the lazy dog
 3187        ˇThe qu«ick bˇ»rown"});
 3188    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3189    cx.assert_editor_state("ˇthe lazy dogˇ");
 3190}
 3191
 3192#[gpui::test]
 3193fn test_delete_line(cx: &mut TestAppContext) {
 3194    init_test(cx, |_| {});
 3195
 3196    let editor = cx.add_window(|window, cx| {
 3197        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3198        build_editor(buffer, window, cx)
 3199    });
 3200    _ = editor.update(cx, |editor, window, cx| {
 3201        editor.change_selections(None, window, cx, |s| {
 3202            s.select_display_ranges([
 3203                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3204                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3205                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3206            ])
 3207        });
 3208        editor.delete_line(&DeleteLine, window, cx);
 3209        assert_eq!(editor.display_text(cx), "ghi");
 3210        assert_eq!(
 3211            editor.selections.display_ranges(cx),
 3212            vec![
 3213                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3214                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3215            ]
 3216        );
 3217    });
 3218
 3219    let editor = cx.add_window(|window, cx| {
 3220        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3221        build_editor(buffer, window, cx)
 3222    });
 3223    _ = editor.update(cx, |editor, window, cx| {
 3224        editor.change_selections(None, window, cx, |s| {
 3225            s.select_display_ranges([
 3226                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3227            ])
 3228        });
 3229        editor.delete_line(&DeleteLine, window, cx);
 3230        assert_eq!(editor.display_text(cx), "ghi\n");
 3231        assert_eq!(
 3232            editor.selections.display_ranges(cx),
 3233            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3234        );
 3235    });
 3236}
 3237
 3238#[gpui::test]
 3239fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3240    init_test(cx, |_| {});
 3241
 3242    cx.add_window(|window, cx| {
 3243        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3244        let mut editor = build_editor(buffer.clone(), window, cx);
 3245        let buffer = buffer.read(cx).as_singleton().unwrap();
 3246
 3247        assert_eq!(
 3248            editor.selections.ranges::<Point>(cx),
 3249            &[Point::new(0, 0)..Point::new(0, 0)]
 3250        );
 3251
 3252        // When on single line, replace newline at end by space
 3253        editor.join_lines(&JoinLines, window, cx);
 3254        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3255        assert_eq!(
 3256            editor.selections.ranges::<Point>(cx),
 3257            &[Point::new(0, 3)..Point::new(0, 3)]
 3258        );
 3259
 3260        // When multiple lines are selected, remove newlines that are spanned by the selection
 3261        editor.change_selections(None, window, cx, |s| {
 3262            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3263        });
 3264        editor.join_lines(&JoinLines, window, cx);
 3265        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3266        assert_eq!(
 3267            editor.selections.ranges::<Point>(cx),
 3268            &[Point::new(0, 11)..Point::new(0, 11)]
 3269        );
 3270
 3271        // Undo should be transactional
 3272        editor.undo(&Undo, window, cx);
 3273        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3274        assert_eq!(
 3275            editor.selections.ranges::<Point>(cx),
 3276            &[Point::new(0, 5)..Point::new(2, 2)]
 3277        );
 3278
 3279        // When joining an empty line don't insert a space
 3280        editor.change_selections(None, window, cx, |s| {
 3281            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3282        });
 3283        editor.join_lines(&JoinLines, window, cx);
 3284        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3285        assert_eq!(
 3286            editor.selections.ranges::<Point>(cx),
 3287            [Point::new(2, 3)..Point::new(2, 3)]
 3288        );
 3289
 3290        // We can remove trailing newlines
 3291        editor.join_lines(&JoinLines, window, cx);
 3292        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3293        assert_eq!(
 3294            editor.selections.ranges::<Point>(cx),
 3295            [Point::new(2, 3)..Point::new(2, 3)]
 3296        );
 3297
 3298        // We don't blow up on the last line
 3299        editor.join_lines(&JoinLines, window, cx);
 3300        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3301        assert_eq!(
 3302            editor.selections.ranges::<Point>(cx),
 3303            [Point::new(2, 3)..Point::new(2, 3)]
 3304        );
 3305
 3306        // reset to test indentation
 3307        editor.buffer.update(cx, |buffer, cx| {
 3308            buffer.edit(
 3309                [
 3310                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3311                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3312                ],
 3313                None,
 3314                cx,
 3315            )
 3316        });
 3317
 3318        // We remove any leading spaces
 3319        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3320        editor.change_selections(None, window, cx, |s| {
 3321            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3322        });
 3323        editor.join_lines(&JoinLines, window, cx);
 3324        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3325
 3326        // We don't insert a space for a line containing only spaces
 3327        editor.join_lines(&JoinLines, window, cx);
 3328        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3329
 3330        // We ignore any leading tabs
 3331        editor.join_lines(&JoinLines, window, cx);
 3332        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3333
 3334        editor
 3335    });
 3336}
 3337
 3338#[gpui::test]
 3339fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3340    init_test(cx, |_| {});
 3341
 3342    cx.add_window(|window, cx| {
 3343        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3344        let mut editor = build_editor(buffer.clone(), window, cx);
 3345        let buffer = buffer.read(cx).as_singleton().unwrap();
 3346
 3347        editor.change_selections(None, window, cx, |s| {
 3348            s.select_ranges([
 3349                Point::new(0, 2)..Point::new(1, 1),
 3350                Point::new(1, 2)..Point::new(1, 2),
 3351                Point::new(3, 1)..Point::new(3, 2),
 3352            ])
 3353        });
 3354
 3355        editor.join_lines(&JoinLines, window, cx);
 3356        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3357
 3358        assert_eq!(
 3359            editor.selections.ranges::<Point>(cx),
 3360            [
 3361                Point::new(0, 7)..Point::new(0, 7),
 3362                Point::new(1, 3)..Point::new(1, 3)
 3363            ]
 3364        );
 3365        editor
 3366    });
 3367}
 3368
 3369#[gpui::test]
 3370async fn test_join_lines_with_git_diff_base(
 3371    executor: BackgroundExecutor,
 3372    cx: &mut gpui::TestAppContext,
 3373) {
 3374    init_test(cx, |_| {});
 3375
 3376    let mut cx = EditorTestContext::new(cx).await;
 3377
 3378    let diff_base = r#"
 3379        Line 0
 3380        Line 1
 3381        Line 2
 3382        Line 3
 3383        "#
 3384    .unindent();
 3385
 3386    cx.set_state(
 3387        &r#"
 3388        ˇLine 0
 3389        Line 1
 3390        Line 2
 3391        Line 3
 3392        "#
 3393        .unindent(),
 3394    );
 3395
 3396    cx.set_diff_base(&diff_base);
 3397    executor.run_until_parked();
 3398
 3399    // Join lines
 3400    cx.update_editor(|editor, window, cx| {
 3401        editor.join_lines(&JoinLines, window, cx);
 3402    });
 3403    executor.run_until_parked();
 3404
 3405    cx.assert_editor_state(
 3406        &r#"
 3407        Line 0ˇ Line 1
 3408        Line 2
 3409        Line 3
 3410        "#
 3411        .unindent(),
 3412    );
 3413    // Join again
 3414    cx.update_editor(|editor, window, cx| {
 3415        editor.join_lines(&JoinLines, window, cx);
 3416    });
 3417    executor.run_until_parked();
 3418
 3419    cx.assert_editor_state(
 3420        &r#"
 3421        Line 0 Line 1ˇ Line 2
 3422        Line 3
 3423        "#
 3424        .unindent(),
 3425    );
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_custom_newlines_cause_no_false_positive_diffs(
 3430    executor: BackgroundExecutor,
 3431    cx: &mut gpui::TestAppContext,
 3432) {
 3433    init_test(cx, |_| {});
 3434    let mut cx = EditorTestContext::new(cx).await;
 3435    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3436    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3437    executor.run_until_parked();
 3438
 3439    cx.update_editor(|editor, window, cx| {
 3440        let snapshot = editor.snapshot(window, cx);
 3441        assert_eq!(
 3442            snapshot
 3443                .buffer_snapshot
 3444                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3445                .collect::<Vec<_>>(),
 3446            Vec::new(),
 3447            "Should not have any diffs for files with custom newlines"
 3448        );
 3449    });
 3450}
 3451
 3452#[gpui::test]
 3453async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3454    init_test(cx, |_| {});
 3455
 3456    let mut cx = EditorTestContext::new(cx).await;
 3457
 3458    // Test sort_lines_case_insensitive()
 3459    cx.set_state(indoc! {"
 3460        «z
 3461        y
 3462        x
 3463        Z
 3464        Y
 3465        Xˇ»
 3466    "});
 3467    cx.update_editor(|e, window, cx| {
 3468        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3469    });
 3470    cx.assert_editor_state(indoc! {"
 3471        «x
 3472        X
 3473        y
 3474        Y
 3475        z
 3476        Zˇ»
 3477    "});
 3478
 3479    // Test reverse_lines()
 3480    cx.set_state(indoc! {"
 3481        «5
 3482        4
 3483        3
 3484        2
 3485        1ˇ»
 3486    "});
 3487    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3488    cx.assert_editor_state(indoc! {"
 3489        «1
 3490        2
 3491        3
 3492        4
 3493        5ˇ»
 3494    "});
 3495
 3496    // Skip testing shuffle_line()
 3497
 3498    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3499    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3500
 3501    // Don't manipulate when cursor is on single line, but expand the selection
 3502    cx.set_state(indoc! {"
 3503        ddˇdd
 3504        ccc
 3505        bb
 3506        a
 3507    "});
 3508    cx.update_editor(|e, window, cx| {
 3509        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3510    });
 3511    cx.assert_editor_state(indoc! {"
 3512        «ddddˇ»
 3513        ccc
 3514        bb
 3515        a
 3516    "});
 3517
 3518    // Basic manipulate case
 3519    // Start selection moves to column 0
 3520    // End of selection shrinks to fit shorter line
 3521    cx.set_state(indoc! {"
 3522        dd«d
 3523        ccc
 3524        bb
 3525        aaaaaˇ»
 3526    "});
 3527    cx.update_editor(|e, window, cx| {
 3528        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3529    });
 3530    cx.assert_editor_state(indoc! {"
 3531        «aaaaa
 3532        bb
 3533        ccc
 3534        dddˇ»
 3535    "});
 3536
 3537    // Manipulate case with newlines
 3538    cx.set_state(indoc! {"
 3539        dd«d
 3540        ccc
 3541
 3542        bb
 3543        aaaaa
 3544
 3545        ˇ»
 3546    "});
 3547    cx.update_editor(|e, window, cx| {
 3548        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3549    });
 3550    cx.assert_editor_state(indoc! {"
 3551        «
 3552
 3553        aaaaa
 3554        bb
 3555        ccc
 3556        dddˇ»
 3557
 3558    "});
 3559
 3560    // Adding new line
 3561    cx.set_state(indoc! {"
 3562        aa«a
 3563        bbˇ»b
 3564    "});
 3565    cx.update_editor(|e, window, cx| {
 3566        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3567    });
 3568    cx.assert_editor_state(indoc! {"
 3569        «aaa
 3570        bbb
 3571        added_lineˇ»
 3572    "});
 3573
 3574    // Removing line
 3575    cx.set_state(indoc! {"
 3576        aa«a
 3577        bbbˇ»
 3578    "});
 3579    cx.update_editor(|e, window, cx| {
 3580        e.manipulate_lines(window, cx, |lines| {
 3581            lines.pop();
 3582        })
 3583    });
 3584    cx.assert_editor_state(indoc! {"
 3585        «aaaˇ»
 3586    "});
 3587
 3588    // Removing all lines
 3589    cx.set_state(indoc! {"
 3590        aa«a
 3591        bbbˇ»
 3592    "});
 3593    cx.update_editor(|e, window, cx| {
 3594        e.manipulate_lines(window, cx, |lines| {
 3595            lines.drain(..);
 3596        })
 3597    });
 3598    cx.assert_editor_state(indoc! {"
 3599        ˇ
 3600    "});
 3601}
 3602
 3603#[gpui::test]
 3604async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3605    init_test(cx, |_| {});
 3606
 3607    let mut cx = EditorTestContext::new(cx).await;
 3608
 3609    // Consider continuous selection as single selection
 3610    cx.set_state(indoc! {"
 3611        Aaa«aa
 3612        cˇ»c«c
 3613        bb
 3614        aaaˇ»aa
 3615    "});
 3616    cx.update_editor(|e, window, cx| {
 3617        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3618    });
 3619    cx.assert_editor_state(indoc! {"
 3620        «Aaaaa
 3621        ccc
 3622        bb
 3623        aaaaaˇ»
 3624    "});
 3625
 3626    cx.set_state(indoc! {"
 3627        Aaa«aa
 3628        cˇ»c«c
 3629        bb
 3630        aaaˇ»aa
 3631    "});
 3632    cx.update_editor(|e, window, cx| {
 3633        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3634    });
 3635    cx.assert_editor_state(indoc! {"
 3636        «Aaaaa
 3637        ccc
 3638        bbˇ»
 3639    "});
 3640
 3641    // Consider non continuous selection as distinct dedup operations
 3642    cx.set_state(indoc! {"
 3643        «aaaaa
 3644        bb
 3645        aaaaa
 3646        aaaaaˇ»
 3647
 3648        aaa«aaˇ»
 3649    "});
 3650    cx.update_editor(|e, window, cx| {
 3651        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3652    });
 3653    cx.assert_editor_state(indoc! {"
 3654        «aaaaa
 3655        bbˇ»
 3656
 3657        «aaaaaˇ»
 3658    "});
 3659}
 3660
 3661#[gpui::test]
 3662async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3663    init_test(cx, |_| {});
 3664
 3665    let mut cx = EditorTestContext::new(cx).await;
 3666
 3667    cx.set_state(indoc! {"
 3668        «Aaa
 3669        aAa
 3670        Aaaˇ»
 3671    "});
 3672    cx.update_editor(|e, window, cx| {
 3673        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3674    });
 3675    cx.assert_editor_state(indoc! {"
 3676        «Aaa
 3677        aAaˇ»
 3678    "});
 3679
 3680    cx.set_state(indoc! {"
 3681        «Aaa
 3682        aAa
 3683        aaAˇ»
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3687    });
 3688    cx.assert_editor_state(indoc! {"
 3689        «Aaaˇ»
 3690    "});
 3691}
 3692
 3693#[gpui::test]
 3694async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3695    init_test(cx, |_| {});
 3696
 3697    let mut cx = EditorTestContext::new(cx).await;
 3698
 3699    // Manipulate with multiple selections on a single line
 3700    cx.set_state(indoc! {"
 3701        dd«dd
 3702        cˇ»c«c
 3703        bb
 3704        aaaˇ»aa
 3705    "});
 3706    cx.update_editor(|e, window, cx| {
 3707        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3708    });
 3709    cx.assert_editor_state(indoc! {"
 3710        «aaaaa
 3711        bb
 3712        ccc
 3713        ddddˇ»
 3714    "});
 3715
 3716    // Manipulate with multiple disjoin selections
 3717    cx.set_state(indoc! {"
 3718 3719        4
 3720        3
 3721        2
 3722        1ˇ»
 3723
 3724        dd«dd
 3725        ccc
 3726        bb
 3727        aaaˇ»aa
 3728    "});
 3729    cx.update_editor(|e, window, cx| {
 3730        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3731    });
 3732    cx.assert_editor_state(indoc! {"
 3733        «1
 3734        2
 3735        3
 3736        4
 3737        5ˇ»
 3738
 3739        «aaaaa
 3740        bb
 3741        ccc
 3742        ddddˇ»
 3743    "});
 3744
 3745    // Adding lines on each selection
 3746    cx.set_state(indoc! {"
 3747 3748        1ˇ»
 3749
 3750        bb«bb
 3751        aaaˇ»aa
 3752    "});
 3753    cx.update_editor(|e, window, cx| {
 3754        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3755    });
 3756    cx.assert_editor_state(indoc! {"
 3757        «2
 3758        1
 3759        added lineˇ»
 3760
 3761        «bbbb
 3762        aaaaa
 3763        added lineˇ»
 3764    "});
 3765
 3766    // Removing lines on each selection
 3767    cx.set_state(indoc! {"
 3768 3769        1ˇ»
 3770
 3771        bb«bb
 3772        aaaˇ»aa
 3773    "});
 3774    cx.update_editor(|e, window, cx| {
 3775        e.manipulate_lines(window, cx, |lines| {
 3776            lines.pop();
 3777        })
 3778    });
 3779    cx.assert_editor_state(indoc! {"
 3780        «2ˇ»
 3781
 3782        «bbbbˇ»
 3783    "});
 3784}
 3785
 3786#[gpui::test]
 3787async fn test_manipulate_text(cx: &mut TestAppContext) {
 3788    init_test(cx, |_| {});
 3789
 3790    let mut cx = EditorTestContext::new(cx).await;
 3791
 3792    // Test convert_to_upper_case()
 3793    cx.set_state(indoc! {"
 3794        «hello worldˇ»
 3795    "});
 3796    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3797    cx.assert_editor_state(indoc! {"
 3798        «HELLO WORLDˇ»
 3799    "});
 3800
 3801    // Test convert_to_lower_case()
 3802    cx.set_state(indoc! {"
 3803        «HELLO WORLDˇ»
 3804    "});
 3805    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3806    cx.assert_editor_state(indoc! {"
 3807        «hello worldˇ»
 3808    "});
 3809
 3810    // Test multiple line, single selection case
 3811    cx.set_state(indoc! {"
 3812        «The quick brown
 3813        fox jumps over
 3814        the lazy dogˇ»
 3815    "});
 3816    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3817    cx.assert_editor_state(indoc! {"
 3818        «The Quick Brown
 3819        Fox Jumps Over
 3820        The Lazy Dogˇ»
 3821    "});
 3822
 3823    // Test multiple line, single selection case
 3824    cx.set_state(indoc! {"
 3825        «The quick brown
 3826        fox jumps over
 3827        the lazy dogˇ»
 3828    "});
 3829    cx.update_editor(|e, window, cx| {
 3830        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3831    });
 3832    cx.assert_editor_state(indoc! {"
 3833        «TheQuickBrown
 3834        FoxJumpsOver
 3835        TheLazyDogˇ»
 3836    "});
 3837
 3838    // From here on out, test more complex cases of manipulate_text()
 3839
 3840    // Test no selection case - should affect words cursors are in
 3841    // Cursor at beginning, middle, and end of word
 3842    cx.set_state(indoc! {"
 3843        ˇhello big beauˇtiful worldˇ
 3844    "});
 3845    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3846    cx.assert_editor_state(indoc! {"
 3847        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3848    "});
 3849
 3850    // Test multiple selections on a single line and across multiple lines
 3851    cx.set_state(indoc! {"
 3852        «Theˇ» quick «brown
 3853        foxˇ» jumps «overˇ»
 3854        the «lazyˇ» dog
 3855    "});
 3856    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3857    cx.assert_editor_state(indoc! {"
 3858        «THEˇ» quick «BROWN
 3859        FOXˇ» jumps «OVERˇ»
 3860        the «LAZYˇ» dog
 3861    "});
 3862
 3863    // Test case where text length grows
 3864    cx.set_state(indoc! {"
 3865        «tschüߡ»
 3866    "});
 3867    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3868    cx.assert_editor_state(indoc! {"
 3869        «TSCHÜSSˇ»
 3870    "});
 3871
 3872    // Test to make sure we don't crash when text shrinks
 3873    cx.set_state(indoc! {"
 3874        aaa_bbbˇ
 3875    "});
 3876    cx.update_editor(|e, window, cx| {
 3877        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3878    });
 3879    cx.assert_editor_state(indoc! {"
 3880        «aaaBbbˇ»
 3881    "});
 3882
 3883    // Test to make sure we all aware of the fact that each word can grow and shrink
 3884    // Final selections should be aware of this fact
 3885    cx.set_state(indoc! {"
 3886        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3887    "});
 3888    cx.update_editor(|e, window, cx| {
 3889        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3890    });
 3891    cx.assert_editor_state(indoc! {"
 3892        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3893    "});
 3894
 3895    cx.set_state(indoc! {"
 3896        «hElLo, WoRld!ˇ»
 3897    "});
 3898    cx.update_editor(|e, window, cx| {
 3899        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3900    });
 3901    cx.assert_editor_state(indoc! {"
 3902        «HeLlO, wOrLD!ˇ»
 3903    "});
 3904}
 3905
 3906#[gpui::test]
 3907fn test_duplicate_line(cx: &mut TestAppContext) {
 3908    init_test(cx, |_| {});
 3909
 3910    let editor = cx.add_window(|window, cx| {
 3911        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3912        build_editor(buffer, window, cx)
 3913    });
 3914    _ = editor.update(cx, |editor, window, cx| {
 3915        editor.change_selections(None, window, cx, |s| {
 3916            s.select_display_ranges([
 3917                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3918                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3919                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3920                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3921            ])
 3922        });
 3923        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3924        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3925        assert_eq!(
 3926            editor.selections.display_ranges(cx),
 3927            vec![
 3928                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3929                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3930                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3931                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3932            ]
 3933        );
 3934    });
 3935
 3936    let editor = cx.add_window(|window, cx| {
 3937        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3938        build_editor(buffer, window, cx)
 3939    });
 3940    _ = editor.update(cx, |editor, window, cx| {
 3941        editor.change_selections(None, window, cx, |s| {
 3942            s.select_display_ranges([
 3943                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3944                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3945            ])
 3946        });
 3947        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3948        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3949        assert_eq!(
 3950            editor.selections.display_ranges(cx),
 3951            vec![
 3952                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3953                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3954            ]
 3955        );
 3956    });
 3957
 3958    // With `move_upwards` the selections stay in place, except for
 3959    // the lines inserted above them
 3960    let editor = cx.add_window(|window, cx| {
 3961        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3962        build_editor(buffer, window, cx)
 3963    });
 3964    _ = editor.update(cx, |editor, window, cx| {
 3965        editor.change_selections(None, window, cx, |s| {
 3966            s.select_display_ranges([
 3967                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3968                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3969                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3970                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3971            ])
 3972        });
 3973        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3974        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3975        assert_eq!(
 3976            editor.selections.display_ranges(cx),
 3977            vec![
 3978                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3979                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3980                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3981                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3982            ]
 3983        );
 3984    });
 3985
 3986    let editor = cx.add_window(|window, cx| {
 3987        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3988        build_editor(buffer, window, cx)
 3989    });
 3990    _ = editor.update(cx, |editor, window, cx| {
 3991        editor.change_selections(None, window, cx, |s| {
 3992            s.select_display_ranges([
 3993                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3994                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3995            ])
 3996        });
 3997        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3998        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3999        assert_eq!(
 4000            editor.selections.display_ranges(cx),
 4001            vec![
 4002                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4003                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4004            ]
 4005        );
 4006    });
 4007
 4008    let editor = cx.add_window(|window, cx| {
 4009        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4010        build_editor(buffer, window, cx)
 4011    });
 4012    _ = editor.update(cx, |editor, window, cx| {
 4013        editor.change_selections(None, window, cx, |s| {
 4014            s.select_display_ranges([
 4015                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4016                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4017            ])
 4018        });
 4019        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4020        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4021        assert_eq!(
 4022            editor.selections.display_ranges(cx),
 4023            vec![
 4024                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4025                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4026            ]
 4027        );
 4028    });
 4029}
 4030
 4031#[gpui::test]
 4032fn test_move_line_up_down(cx: &mut TestAppContext) {
 4033    init_test(cx, |_| {});
 4034
 4035    let editor = cx.add_window(|window, cx| {
 4036        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4037        build_editor(buffer, window, cx)
 4038    });
 4039    _ = editor.update(cx, |editor, window, cx| {
 4040        editor.fold_creases(
 4041            vec![
 4042                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4043                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4044                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4045            ],
 4046            true,
 4047            window,
 4048            cx,
 4049        );
 4050        editor.change_selections(None, window, cx, |s| {
 4051            s.select_display_ranges([
 4052                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4053                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4054                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4055                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4056            ])
 4057        });
 4058        assert_eq!(
 4059            editor.display_text(cx),
 4060            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4061        );
 4062
 4063        editor.move_line_up(&MoveLineUp, window, cx);
 4064        assert_eq!(
 4065            editor.display_text(cx),
 4066            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4067        );
 4068        assert_eq!(
 4069            editor.selections.display_ranges(cx),
 4070            vec![
 4071                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4072                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4073                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4074                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4075            ]
 4076        );
 4077    });
 4078
 4079    _ = editor.update(cx, |editor, window, cx| {
 4080        editor.move_line_down(&MoveLineDown, window, cx);
 4081        assert_eq!(
 4082            editor.display_text(cx),
 4083            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4084        );
 4085        assert_eq!(
 4086            editor.selections.display_ranges(cx),
 4087            vec![
 4088                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4089                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4090                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4091                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4092            ]
 4093        );
 4094    });
 4095
 4096    _ = editor.update(cx, |editor, window, cx| {
 4097        editor.move_line_down(&MoveLineDown, window, cx);
 4098        assert_eq!(
 4099            editor.display_text(cx),
 4100            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4101        );
 4102        assert_eq!(
 4103            editor.selections.display_ranges(cx),
 4104            vec![
 4105                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4106                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4107                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4108                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4109            ]
 4110        );
 4111    });
 4112
 4113    _ = editor.update(cx, |editor, window, cx| {
 4114        editor.move_line_up(&MoveLineUp, window, cx);
 4115        assert_eq!(
 4116            editor.display_text(cx),
 4117            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4118        );
 4119        assert_eq!(
 4120            editor.selections.display_ranges(cx),
 4121            vec![
 4122                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4123                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4124                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4125                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4126            ]
 4127        );
 4128    });
 4129}
 4130
 4131#[gpui::test]
 4132fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4133    init_test(cx, |_| {});
 4134
 4135    let editor = cx.add_window(|window, cx| {
 4136        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4137        build_editor(buffer, window, cx)
 4138    });
 4139    _ = editor.update(cx, |editor, window, cx| {
 4140        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4141        editor.insert_blocks(
 4142            [BlockProperties {
 4143                style: BlockStyle::Fixed,
 4144                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4145                height: 1,
 4146                render: Arc::new(|_| div().into_any()),
 4147                priority: 0,
 4148            }],
 4149            Some(Autoscroll::fit()),
 4150            cx,
 4151        );
 4152        editor.change_selections(None, window, cx, |s| {
 4153            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4154        });
 4155        editor.move_line_down(&MoveLineDown, window, cx);
 4156    });
 4157}
 4158
 4159#[gpui::test]
 4160async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4161    init_test(cx, |_| {});
 4162
 4163    let mut cx = EditorTestContext::new(cx).await;
 4164    cx.set_state(
 4165        &"
 4166            ˇzero
 4167            one
 4168            two
 4169            three
 4170            four
 4171            five
 4172        "
 4173        .unindent(),
 4174    );
 4175
 4176    // Create a four-line block that replaces three lines of text.
 4177    cx.update_editor(|editor, window, cx| {
 4178        let snapshot = editor.snapshot(window, cx);
 4179        let snapshot = &snapshot.buffer_snapshot;
 4180        let placement = BlockPlacement::Replace(
 4181            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4182        );
 4183        editor.insert_blocks(
 4184            [BlockProperties {
 4185                placement,
 4186                height: 4,
 4187                style: BlockStyle::Sticky,
 4188                render: Arc::new(|_| gpui::div().into_any_element()),
 4189                priority: 0,
 4190            }],
 4191            None,
 4192            cx,
 4193        );
 4194    });
 4195
 4196    // Move down so that the cursor touches the block.
 4197    cx.update_editor(|editor, window, cx| {
 4198        editor.move_down(&Default::default(), window, cx);
 4199    });
 4200    cx.assert_editor_state(
 4201        &"
 4202            zero
 4203            «one
 4204            two
 4205            threeˇ»
 4206            four
 4207            five
 4208        "
 4209        .unindent(),
 4210    );
 4211
 4212    // Move down past the block.
 4213    cx.update_editor(|editor, window, cx| {
 4214        editor.move_down(&Default::default(), window, cx);
 4215    });
 4216    cx.assert_editor_state(
 4217        &"
 4218            zero
 4219            one
 4220            two
 4221            three
 4222            ˇfour
 4223            five
 4224        "
 4225        .unindent(),
 4226    );
 4227}
 4228
 4229#[gpui::test]
 4230fn test_transpose(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    _ = cx.add_window(|window, cx| {
 4234        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4235        editor.set_style(EditorStyle::default(), window, cx);
 4236        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4237        editor.transpose(&Default::default(), window, cx);
 4238        assert_eq!(editor.text(cx), "bac");
 4239        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4240
 4241        editor.transpose(&Default::default(), window, cx);
 4242        assert_eq!(editor.text(cx), "bca");
 4243        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4244
 4245        editor.transpose(&Default::default(), window, cx);
 4246        assert_eq!(editor.text(cx), "bac");
 4247        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4248
 4249        editor
 4250    });
 4251
 4252    _ = cx.add_window(|window, cx| {
 4253        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4254        editor.set_style(EditorStyle::default(), window, cx);
 4255        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4256        editor.transpose(&Default::default(), window, cx);
 4257        assert_eq!(editor.text(cx), "acb\nde");
 4258        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4259
 4260        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4261        editor.transpose(&Default::default(), window, cx);
 4262        assert_eq!(editor.text(cx), "acbd\ne");
 4263        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4264
 4265        editor.transpose(&Default::default(), window, cx);
 4266        assert_eq!(editor.text(cx), "acbde\n");
 4267        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4268
 4269        editor.transpose(&Default::default(), window, cx);
 4270        assert_eq!(editor.text(cx), "acbd\ne");
 4271        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4272
 4273        editor
 4274    });
 4275
 4276    _ = cx.add_window(|window, cx| {
 4277        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4278        editor.set_style(EditorStyle::default(), window, cx);
 4279        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4280        editor.transpose(&Default::default(), window, cx);
 4281        assert_eq!(editor.text(cx), "bacd\ne");
 4282        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4283
 4284        editor.transpose(&Default::default(), window, cx);
 4285        assert_eq!(editor.text(cx), "bcade\n");
 4286        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4287
 4288        editor.transpose(&Default::default(), window, cx);
 4289        assert_eq!(editor.text(cx), "bcda\ne");
 4290        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4291
 4292        editor.transpose(&Default::default(), window, cx);
 4293        assert_eq!(editor.text(cx), "bcade\n");
 4294        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4295
 4296        editor.transpose(&Default::default(), window, cx);
 4297        assert_eq!(editor.text(cx), "bcaed\n");
 4298        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4299
 4300        editor
 4301    });
 4302
 4303    _ = cx.add_window(|window, cx| {
 4304        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4305        editor.set_style(EditorStyle::default(), window, cx);
 4306        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4307        editor.transpose(&Default::default(), window, cx);
 4308        assert_eq!(editor.text(cx), "🏀🍐✋");
 4309        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4310
 4311        editor.transpose(&Default::default(), window, cx);
 4312        assert_eq!(editor.text(cx), "🏀✋🍐");
 4313        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4314
 4315        editor.transpose(&Default::default(), window, cx);
 4316        assert_eq!(editor.text(cx), "🏀🍐✋");
 4317        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4318
 4319        editor
 4320    });
 4321}
 4322
 4323#[gpui::test]
 4324async fn test_rewrap(cx: &mut TestAppContext) {
 4325    init_test(cx, |_| {});
 4326
 4327    let mut cx = EditorTestContext::new(cx).await;
 4328
 4329    let language_with_c_comments = Arc::new(Language::new(
 4330        LanguageConfig {
 4331            line_comments: vec!["// ".into()],
 4332            ..LanguageConfig::default()
 4333        },
 4334        None,
 4335    ));
 4336    let language_with_pound_comments = Arc::new(Language::new(
 4337        LanguageConfig {
 4338            line_comments: vec!["# ".into()],
 4339            ..LanguageConfig::default()
 4340        },
 4341        None,
 4342    ));
 4343    let markdown_language = Arc::new(Language::new(
 4344        LanguageConfig {
 4345            name: "Markdown".into(),
 4346            ..LanguageConfig::default()
 4347        },
 4348        None,
 4349    ));
 4350    let language_with_doc_comments = Arc::new(Language::new(
 4351        LanguageConfig {
 4352            line_comments: vec!["// ".into(), "/// ".into()],
 4353            ..LanguageConfig::default()
 4354        },
 4355        Some(tree_sitter_rust::LANGUAGE.into()),
 4356    ));
 4357
 4358    let plaintext_language = Arc::new(Language::new(
 4359        LanguageConfig {
 4360            name: "Plain Text".into(),
 4361            ..LanguageConfig::default()
 4362        },
 4363        None,
 4364    ));
 4365
 4366    assert_rewrap(
 4367        indoc! {"
 4368            // ˇ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.
 4369        "},
 4370        indoc! {"
 4371            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4372            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4373            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4374            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4375            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4376            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4377            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4378            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4379            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4380            // porttitor id. Aliquam id accumsan eros.
 4381        "},
 4382        language_with_c_comments.clone(),
 4383        &mut cx,
 4384    );
 4385
 4386    // Test that rewrapping works inside of a selection
 4387    assert_rewrap(
 4388        indoc! {"
 4389            «// 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.ˇ»
 4390        "},
 4391        indoc! {"
 4392            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4393            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4394            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4395            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4396            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4397            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4398            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4399            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4400            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4401            // porttitor id. Aliquam id accumsan eros.ˇ»
 4402        "},
 4403        language_with_c_comments.clone(),
 4404        &mut cx,
 4405    );
 4406
 4407    // Test that cursors that expand to the same region are collapsed.
 4408    assert_rewrap(
 4409        indoc! {"
 4410            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4411            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4412            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4413            // ˇ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.
 4414        "},
 4415        indoc! {"
 4416            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4417            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4418            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4419            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4420            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4421            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4422            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4423            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4424            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4425            // porttitor id. Aliquam id accumsan eros.
 4426        "},
 4427        language_with_c_comments.clone(),
 4428        &mut cx,
 4429    );
 4430
 4431    // Test that non-contiguous selections are treated separately.
 4432    assert_rewrap(
 4433        indoc! {"
 4434            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4435            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4436            //
 4437            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4438            // ˇ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.
 4439        "},
 4440        indoc! {"
 4441            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4442            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4443            // auctor, eu lacinia sapien scelerisque.
 4444            //
 4445            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4446            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4447            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4448            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4449            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4450            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4451            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4452        "},
 4453        language_with_c_comments.clone(),
 4454        &mut cx,
 4455    );
 4456
 4457    // Test that different comment prefixes are supported.
 4458    assert_rewrap(
 4459        indoc! {"
 4460            # ˇ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.
 4461        "},
 4462        indoc! {"
 4463            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4464            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4465            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4466            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4467            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4468            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4469            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4470            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4471            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4472            # accumsan eros.
 4473        "},
 4474        language_with_pound_comments.clone(),
 4475        &mut cx,
 4476    );
 4477
 4478    // Test that rewrapping is ignored outside of comments in most languages.
 4479    assert_rewrap(
 4480        indoc! {"
 4481            /// Adds two numbers.
 4482            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4483            fn add(a: u32, b: u32) -> u32 {
 4484                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ˇ
 4485            }
 4486        "},
 4487        indoc! {"
 4488            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4489            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4490            fn add(a: u32, b: u32) -> u32 {
 4491                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ˇ
 4492            }
 4493        "},
 4494        language_with_doc_comments.clone(),
 4495        &mut cx,
 4496    );
 4497
 4498    // Test that rewrapping works in Markdown and Plain Text languages.
 4499    assert_rewrap(
 4500        indoc! {"
 4501            # Hello
 4502
 4503            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.
 4504        "},
 4505        indoc! {"
 4506            # Hello
 4507
 4508            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4509            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4510            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4511            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4512            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4513            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4514            Integer sit amet scelerisque nisi.
 4515        "},
 4516        markdown_language,
 4517        &mut cx,
 4518    );
 4519
 4520    assert_rewrap(
 4521        indoc! {"
 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            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4526            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4527            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4528            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4529            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4530            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4531            Integer sit amet scelerisque nisi.
 4532        "},
 4533        plaintext_language,
 4534        &mut cx,
 4535    );
 4536
 4537    // Test rewrapping unaligned comments in a selection.
 4538    assert_rewrap(
 4539        indoc! {"
 4540            fn foo() {
 4541                if true {
 4542            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4543            // Praesent semper egestas tellus id dignissim.ˇ»
 4544                    do_something();
 4545                } else {
 4546                    //
 4547                }
 4548            }
 4549        "},
 4550        indoc! {"
 4551            fn foo() {
 4552                if true {
 4553            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4554                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4555                    // egestas tellus id dignissim.ˇ»
 4556                    do_something();
 4557                } else {
 4558                    //
 4559                }
 4560            }
 4561        "},
 4562        language_with_doc_comments.clone(),
 4563        &mut cx,
 4564    );
 4565
 4566    assert_rewrap(
 4567        indoc! {"
 4568            fn foo() {
 4569                if true {
 4570            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4571            // Praesent semper egestas tellus id dignissim.»
 4572                    do_something();
 4573                } else {
 4574                    //
 4575                }
 4576
 4577            }
 4578        "},
 4579        indoc! {"
 4580            fn foo() {
 4581                if true {
 4582            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4583                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4584                    // egestas tellus id dignissim.»
 4585                    do_something();
 4586                } else {
 4587                    //
 4588                }
 4589
 4590            }
 4591        "},
 4592        language_with_doc_comments.clone(),
 4593        &mut cx,
 4594    );
 4595
 4596    #[track_caller]
 4597    fn assert_rewrap(
 4598        unwrapped_text: &str,
 4599        wrapped_text: &str,
 4600        language: Arc<Language>,
 4601        cx: &mut EditorTestContext,
 4602    ) {
 4603        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4604        cx.set_state(unwrapped_text);
 4605        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4606        cx.assert_editor_state(wrapped_text);
 4607    }
 4608}
 4609
 4610#[gpui::test]
 4611async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4612    init_test(cx, |_| {});
 4613
 4614    let mut cx = EditorTestContext::new(cx).await;
 4615
 4616    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4617    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4618    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4619
 4620    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4621    cx.set_state("two ˇfour ˇsix ˇ");
 4622    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4623    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4624
 4625    // Paste again but with only two cursors. Since the number of cursors doesn't
 4626    // match the number of slices in the clipboard, the entire clipboard text
 4627    // is pasted at each cursor.
 4628    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4629    cx.update_editor(|e, window, cx| {
 4630        e.handle_input("( ", window, cx);
 4631        e.paste(&Paste, window, cx);
 4632        e.handle_input(") ", window, cx);
 4633    });
 4634    cx.assert_editor_state(
 4635        &([
 4636            "( one✅ ",
 4637            "three ",
 4638            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4639            "three ",
 4640            "five ) ˇ",
 4641        ]
 4642        .join("\n")),
 4643    );
 4644
 4645    // Cut with three selections, one of which is full-line.
 4646    cx.set_state(indoc! {"
 4647        1«2ˇ»3
 4648        4ˇ567
 4649        «8ˇ»9"});
 4650    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4651    cx.assert_editor_state(indoc! {"
 4652        1ˇ3
 4653        ˇ9"});
 4654
 4655    // Paste with three selections, noticing how the copied selection that was full-line
 4656    // gets inserted before the second cursor.
 4657    cx.set_state(indoc! {"
 4658        1ˇ3
 4659 4660        «oˇ»ne"});
 4661    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4662    cx.assert_editor_state(indoc! {"
 4663        12ˇ3
 4664        4567
 4665 4666        8ˇne"});
 4667
 4668    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4669    cx.set_state(indoc! {"
 4670        The quick brown
 4671        fox juˇmps over
 4672        the lazy dog"});
 4673    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4674    assert_eq!(
 4675        cx.read_from_clipboard()
 4676            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4677        Some("fox jumps over\n".to_string())
 4678    );
 4679
 4680    // Paste with three selections, noticing how the copied full-line selection is inserted
 4681    // before the empty selections but replaces the selection that is non-empty.
 4682    cx.set_state(indoc! {"
 4683        Tˇhe quick brown
 4684        «foˇ»x jumps over
 4685        tˇhe lazy dog"});
 4686    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4687    cx.assert_editor_state(indoc! {"
 4688        fox jumps over
 4689        Tˇhe quick brown
 4690        fox jumps over
 4691        ˇx jumps over
 4692        fox jumps over
 4693        tˇhe lazy dog"});
 4694}
 4695
 4696#[gpui::test]
 4697async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4698    init_test(cx, |_| {});
 4699
 4700    let mut cx = EditorTestContext::new(cx).await;
 4701    let language = Arc::new(Language::new(
 4702        LanguageConfig::default(),
 4703        Some(tree_sitter_rust::LANGUAGE.into()),
 4704    ));
 4705    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4706
 4707    // Cut an indented block, without the leading whitespace.
 4708    cx.set_state(indoc! {"
 4709        const a: B = (
 4710            c(),
 4711            «d(
 4712                e,
 4713                f
 4714            )ˇ»
 4715        );
 4716    "});
 4717    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4718    cx.assert_editor_state(indoc! {"
 4719        const a: B = (
 4720            c(),
 4721            ˇ
 4722        );
 4723    "});
 4724
 4725    // Paste it at the same position.
 4726    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4727    cx.assert_editor_state(indoc! {"
 4728        const a: B = (
 4729            c(),
 4730            d(
 4731                e,
 4732                f
 4733 4734        );
 4735    "});
 4736
 4737    // Paste it at a line with a lower indent level.
 4738    cx.set_state(indoc! {"
 4739        ˇ
 4740        const a: B = (
 4741            c(),
 4742        );
 4743    "});
 4744    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4745    cx.assert_editor_state(indoc! {"
 4746        d(
 4747            e,
 4748            f
 4749 4750        const a: B = (
 4751            c(),
 4752        );
 4753    "});
 4754
 4755    // Cut an indented block, with the leading whitespace.
 4756    cx.set_state(indoc! {"
 4757        const a: B = (
 4758            c(),
 4759        «    d(
 4760                e,
 4761                f
 4762            )
 4763        ˇ»);
 4764    "});
 4765    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4766    cx.assert_editor_state(indoc! {"
 4767        const a: B = (
 4768            c(),
 4769        ˇ);
 4770    "});
 4771
 4772    // Paste it at the same position.
 4773    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4774    cx.assert_editor_state(indoc! {"
 4775        const a: B = (
 4776            c(),
 4777            d(
 4778                e,
 4779                f
 4780            )
 4781        ˇ);
 4782    "});
 4783
 4784    // Paste it at a line with a higher indent level.
 4785    cx.set_state(indoc! {"
 4786        const a: B = (
 4787            c(),
 4788            d(
 4789                e,
 4790 4791            )
 4792        );
 4793    "});
 4794    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4795    cx.assert_editor_state(indoc! {"
 4796        const a: B = (
 4797            c(),
 4798            d(
 4799                e,
 4800                f    d(
 4801                    e,
 4802                    f
 4803                )
 4804        ˇ
 4805            )
 4806        );
 4807    "});
 4808}
 4809
 4810#[gpui::test]
 4811fn test_select_all(cx: &mut TestAppContext) {
 4812    init_test(cx, |_| {});
 4813
 4814    let editor = cx.add_window(|window, cx| {
 4815        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4816        build_editor(buffer, window, cx)
 4817    });
 4818    _ = editor.update(cx, |editor, window, cx| {
 4819        editor.select_all(&SelectAll, window, cx);
 4820        assert_eq!(
 4821            editor.selections.display_ranges(cx),
 4822            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4823        );
 4824    });
 4825}
 4826
 4827#[gpui::test]
 4828fn test_select_line(cx: &mut TestAppContext) {
 4829    init_test(cx, |_| {});
 4830
 4831    let editor = cx.add_window(|window, cx| {
 4832        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4833        build_editor(buffer, window, cx)
 4834    });
 4835    _ = editor.update(cx, |editor, window, cx| {
 4836        editor.change_selections(None, window, cx, |s| {
 4837            s.select_display_ranges([
 4838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4839                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4840                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4841                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4842            ])
 4843        });
 4844        editor.select_line(&SelectLine, window, cx);
 4845        assert_eq!(
 4846            editor.selections.display_ranges(cx),
 4847            vec![
 4848                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4849                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4850            ]
 4851        );
 4852    });
 4853
 4854    _ = editor.update(cx, |editor, window, cx| {
 4855        editor.select_line(&SelectLine, window, cx);
 4856        assert_eq!(
 4857            editor.selections.display_ranges(cx),
 4858            vec![
 4859                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4860                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4861            ]
 4862        );
 4863    });
 4864
 4865    _ = editor.update(cx, |editor, window, cx| {
 4866        editor.select_line(&SelectLine, window, cx);
 4867        assert_eq!(
 4868            editor.selections.display_ranges(cx),
 4869            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4870        );
 4871    });
 4872}
 4873
 4874#[gpui::test]
 4875fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4876    init_test(cx, |_| {});
 4877
 4878    let editor = cx.add_window(|window, cx| {
 4879        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4880        build_editor(buffer, window, cx)
 4881    });
 4882    _ = editor.update(cx, |editor, window, cx| {
 4883        editor.fold_creases(
 4884            vec![
 4885                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4886                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4887                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4888            ],
 4889            true,
 4890            window,
 4891            cx,
 4892        );
 4893        editor.change_selections(None, window, cx, |s| {
 4894            s.select_display_ranges([
 4895                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4896                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4897                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4898                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4899            ])
 4900        });
 4901        assert_eq!(
 4902            editor.display_text(cx),
 4903            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4904        );
 4905    });
 4906
 4907    _ = editor.update(cx, |editor, window, cx| {
 4908        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4909        assert_eq!(
 4910            editor.display_text(cx),
 4911            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4912        );
 4913        assert_eq!(
 4914            editor.selections.display_ranges(cx),
 4915            [
 4916                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4917                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4918                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4919                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4920            ]
 4921        );
 4922    });
 4923
 4924    _ = editor.update(cx, |editor, window, cx| {
 4925        editor.change_selections(None, window, cx, |s| {
 4926            s.select_display_ranges([
 4927                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4928            ])
 4929        });
 4930        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4931        assert_eq!(
 4932            editor.display_text(cx),
 4933            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4934        );
 4935        assert_eq!(
 4936            editor.selections.display_ranges(cx),
 4937            [
 4938                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4939                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4940                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4941                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4942                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4943                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4944                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4945                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4946            ]
 4947        );
 4948    });
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let mut cx = EditorTestContext::new(cx).await;
 4956
 4957    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4958    cx.set_state(indoc!(
 4959        r#"abc
 4960           defˇghi
 4961
 4962           jk
 4963           nlmo
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|editor, window, cx| {
 4968        editor.add_selection_above(&Default::default(), window, cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abcˇ
 4973           defˇghi
 4974
 4975           jk
 4976           nlmo
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|editor, window, cx| {
 4981        editor.add_selection_above(&Default::default(), window, cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abcˇ
 4986            defˇghi
 4987
 4988            jk
 4989            nlmo
 4990            "#
 4991    ));
 4992
 4993    cx.update_editor(|editor, window, cx| {
 4994        editor.add_selection_below(&Default::default(), window, cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"abc
 4999           defˇghi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    cx.update_editor(|editor, window, cx| {
 5007        editor.undo_selection(&Default::default(), window, cx);
 5008    });
 5009
 5010    cx.assert_editor_state(indoc!(
 5011        r#"abcˇ
 5012           defˇghi
 5013
 5014           jk
 5015           nlmo
 5016           "#
 5017    ));
 5018
 5019    cx.update_editor(|editor, window, cx| {
 5020        editor.redo_selection(&Default::default(), window, cx);
 5021    });
 5022
 5023    cx.assert_editor_state(indoc!(
 5024        r#"abc
 5025           defˇghi
 5026
 5027           jk
 5028           nlmo
 5029           "#
 5030    ));
 5031
 5032    cx.update_editor(|editor, window, cx| {
 5033        editor.add_selection_below(&Default::default(), window, cx);
 5034    });
 5035
 5036    cx.assert_editor_state(indoc!(
 5037        r#"abc
 5038           defˇghi
 5039
 5040           jk
 5041           nlmˇo
 5042           "#
 5043    ));
 5044
 5045    cx.update_editor(|editor, window, cx| {
 5046        editor.add_selection_below(&Default::default(), window, cx);
 5047    });
 5048
 5049    cx.assert_editor_state(indoc!(
 5050        r#"abc
 5051           defˇghi
 5052
 5053           jk
 5054           nlmˇo
 5055           "#
 5056    ));
 5057
 5058    // change selections
 5059    cx.set_state(indoc!(
 5060        r#"abc
 5061           def«ˇg»hi
 5062
 5063           jk
 5064           nlmo
 5065           "#
 5066    ));
 5067
 5068    cx.update_editor(|editor, window, cx| {
 5069        editor.add_selection_below(&Default::default(), window, cx);
 5070    });
 5071
 5072    cx.assert_editor_state(indoc!(
 5073        r#"abc
 5074           def«ˇg»hi
 5075
 5076           jk
 5077           nlm«ˇo»
 5078           "#
 5079    ));
 5080
 5081    cx.update_editor(|editor, window, cx| {
 5082        editor.add_selection_below(&Default::default(), window, cx);
 5083    });
 5084
 5085    cx.assert_editor_state(indoc!(
 5086        r#"abc
 5087           def«ˇg»hi
 5088
 5089           jk
 5090           nlm«ˇo»
 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«ˇg»hi
 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«ˇg»hi
 5114
 5115           jk
 5116           nlmo
 5117           "#
 5118    ));
 5119
 5120    // Change selections again
 5121    cx.set_state(indoc!(
 5122        r#"a«bc
 5123           defgˇ»hi
 5124
 5125           jk
 5126           nlmo
 5127           "#
 5128    ));
 5129
 5130    cx.update_editor(|editor, window, cx| {
 5131        editor.add_selection_below(&Default::default(), window, cx);
 5132    });
 5133
 5134    cx.assert_editor_state(indoc!(
 5135        r#"a«bcˇ»
 5136           d«efgˇ»hi
 5137
 5138           j«kˇ»
 5139           nlmo
 5140           "#
 5141    ));
 5142
 5143    cx.update_editor(|editor, window, cx| {
 5144        editor.add_selection_below(&Default::default(), window, cx);
 5145    });
 5146    cx.assert_editor_state(indoc!(
 5147        r#"a«bcˇ»
 5148           d«efgˇ»hi
 5149
 5150           j«kˇ»
 5151           n«lmoˇ»
 5152           "#
 5153    ));
 5154    cx.update_editor(|editor, window, cx| {
 5155        editor.add_selection_above(&Default::default(), window, cx);
 5156    });
 5157
 5158    cx.assert_editor_state(indoc!(
 5159        r#"a«bcˇ»
 5160           d«efgˇ»hi
 5161
 5162           j«kˇ»
 5163           nlmo
 5164           "#
 5165    ));
 5166
 5167    // Change selections again
 5168    cx.set_state(indoc!(
 5169        r#"abc
 5170           d«ˇefghi
 5171
 5172           jk
 5173           nlm»o
 5174           "#
 5175    ));
 5176
 5177    cx.update_editor(|editor, window, cx| {
 5178        editor.add_selection_above(&Default::default(), window, cx);
 5179    });
 5180
 5181    cx.assert_editor_state(indoc!(
 5182        r#"a«ˇbc»
 5183           d«ˇef»ghi
 5184
 5185           j«ˇk»
 5186           n«ˇlm»o
 5187           "#
 5188    ));
 5189
 5190    cx.update_editor(|editor, window, cx| {
 5191        editor.add_selection_below(&Default::default(), window, cx);
 5192    });
 5193
 5194    cx.assert_editor_state(indoc!(
 5195        r#"abc
 5196           d«ˇef»ghi
 5197
 5198           j«ˇk»
 5199           n«ˇlm»o
 5200           "#
 5201    ));
 5202}
 5203
 5204#[gpui::test]
 5205async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5206    init_test(cx, |_| {});
 5207
 5208    let mut cx = EditorTestContext::new(cx).await;
 5209    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5210
 5211    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5212        .unwrap();
 5213    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5214
 5215    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5216        .unwrap();
 5217    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5218
 5219    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5220    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5221
 5222    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5223    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5224
 5225    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5226        .unwrap();
 5227    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5228
 5229    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5230        .unwrap();
 5231    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5232}
 5233
 5234#[gpui::test]
 5235async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5236    init_test(cx, |_| {});
 5237
 5238    let mut cx = EditorTestContext::new(cx).await;
 5239
 5240    // Test caret-only selections
 5241    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5242
 5243    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5244        .unwrap();
 5245    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5246
 5247    // Test left-to-right selections
 5248    cx.set_state("abc\n«abcˇ»\nabc");
 5249
 5250    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5251        .unwrap();
 5252    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5253
 5254    // Test right-to-left selections
 5255    cx.set_state("abc\n«ˇabc»\nabc");
 5256
 5257    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5258        .unwrap();
 5259    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5260}
 5261
 5262#[gpui::test]
 5263async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5264    init_test(cx, |_| {});
 5265
 5266    let mut cx = EditorTestContext::new(cx).await;
 5267    cx.set_state(
 5268        r#"let foo = 2;
 5269lˇet foo = 2;
 5270let fooˇ = 2;
 5271let foo = 2;
 5272let foo = ˇ2;"#,
 5273    );
 5274
 5275    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5276        .unwrap();
 5277    cx.assert_editor_state(
 5278        r#"let foo = 2;
 5279«letˇ» foo = 2;
 5280let «fooˇ» = 2;
 5281let foo = 2;
 5282let foo = «2ˇ»;"#,
 5283    );
 5284
 5285    // noop for multiple selections with different contents
 5286    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5287        .unwrap();
 5288    cx.assert_editor_state(
 5289        r#"let foo = 2;
 5290«letˇ» foo = 2;
 5291let «fooˇ» = 2;
 5292let foo = 2;
 5293let foo = «2ˇ»;"#,
 5294    );
 5295}
 5296
 5297#[gpui::test]
 5298async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5299    init_test(cx, |_| {});
 5300
 5301    let mut cx =
 5302        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5303
 5304    cx.assert_editor_state(indoc! {"
 5305        ˇbbb
 5306        ccc
 5307
 5308        bbb
 5309        ccc
 5310        "});
 5311    cx.dispatch_action(SelectPrevious::default());
 5312    cx.assert_editor_state(indoc! {"
 5313                «bbbˇ»
 5314                ccc
 5315
 5316                bbb
 5317                ccc
 5318                "});
 5319    cx.dispatch_action(SelectPrevious::default());
 5320    cx.assert_editor_state(indoc! {"
 5321                «bbbˇ»
 5322                ccc
 5323
 5324                «bbbˇ»
 5325                ccc
 5326                "});
 5327}
 5328
 5329#[gpui::test]
 5330async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5331    init_test(cx, |_| {});
 5332
 5333    let mut cx = EditorTestContext::new(cx).await;
 5334    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5335
 5336    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5337        .unwrap();
 5338    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5339
 5340    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5341        .unwrap();
 5342    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5343
 5344    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5345    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5346
 5347    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5348    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5349
 5350    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5351        .unwrap();
 5352    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5353
 5354    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5355        .unwrap();
 5356    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5357
 5358    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5359        .unwrap();
 5360    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5361}
 5362
 5363#[gpui::test]
 5364async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5365    init_test(cx, |_| {});
 5366
 5367    let mut cx = EditorTestContext::new(cx).await;
 5368    cx.set_state(
 5369        r#"let foo = 2;
 5370lˇet foo = 2;
 5371let fooˇ = 2;
 5372let foo = 2;
 5373let foo = ˇ2;"#,
 5374    );
 5375
 5376    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5377        .unwrap();
 5378    cx.assert_editor_state(
 5379        r#"let foo = 2;
 5380«letˇ» foo = 2;
 5381let «fooˇ» = 2;
 5382let foo = 2;
 5383let foo = «2ˇ»;"#,
 5384    );
 5385
 5386    // noop for multiple selections with different contents
 5387    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5388        .unwrap();
 5389    cx.assert_editor_state(
 5390        r#"let foo = 2;
 5391«letˇ» foo = 2;
 5392let «fooˇ» = 2;
 5393let foo = 2;
 5394let foo = «2ˇ»;"#,
 5395    );
 5396}
 5397
 5398#[gpui::test]
 5399async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5400    init_test(cx, |_| {});
 5401
 5402    let mut cx = EditorTestContext::new(cx).await;
 5403    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5404
 5405    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5406        .unwrap();
 5407    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5408
 5409    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5410        .unwrap();
 5411    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5412
 5413    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5414    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5415
 5416    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5417    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5418
 5419    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5420        .unwrap();
 5421    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5422
 5423    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5424        .unwrap();
 5425    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5426}
 5427
 5428#[gpui::test]
 5429async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5430    init_test(cx, |_| {});
 5431
 5432    let language = Arc::new(Language::new(
 5433        LanguageConfig::default(),
 5434        Some(tree_sitter_rust::LANGUAGE.into()),
 5435    ));
 5436
 5437    let text = r#"
 5438        use mod1::mod2::{mod3, mod4};
 5439
 5440        fn fn_1(param1: bool, param2: &str) {
 5441            let var1 = "text";
 5442        }
 5443    "#
 5444    .unindent();
 5445
 5446    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5447    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5448    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5449
 5450    editor
 5451        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5452        .await;
 5453
 5454    editor.update_in(cx, |editor, window, cx| {
 5455        editor.change_selections(None, window, cx, |s| {
 5456            s.select_display_ranges([
 5457                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5458                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5459                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5460            ]);
 5461        });
 5462        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5463    });
 5464    editor.update(cx, |editor, cx| {
 5465        assert_text_with_selections(
 5466            editor,
 5467            indoc! {r#"
 5468                use mod1::mod2::{mod3, «mod4ˇ»};
 5469
 5470                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5471                    let var1 = "«textˇ»";
 5472                }
 5473            "#},
 5474            cx,
 5475        );
 5476    });
 5477
 5478    editor.update_in(cx, |editor, window, cx| {
 5479        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5480    });
 5481    editor.update(cx, |editor, cx| {
 5482        assert_text_with_selections(
 5483            editor,
 5484            indoc! {r#"
 5485                use mod1::mod2::«{mod3, mod4}ˇ»;
 5486
 5487                «ˇfn fn_1(param1: bool, param2: &str) {
 5488                    let var1 = "text";
 5489 5490            "#},
 5491            cx,
 5492        );
 5493    });
 5494
 5495    editor.update_in(cx, |editor, window, cx| {
 5496        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5497    });
 5498    assert_eq!(
 5499        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5500        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5501    );
 5502
 5503    // Trying to expand the selected syntax node one more time has no effect.
 5504    editor.update_in(cx, |editor, window, cx| {
 5505        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5506    });
 5507    assert_eq!(
 5508        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5509        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5510    );
 5511
 5512    editor.update_in(cx, |editor, window, cx| {
 5513        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5514    });
 5515    editor.update(cx, |editor, cx| {
 5516        assert_text_with_selections(
 5517            editor,
 5518            indoc! {r#"
 5519                use mod1::mod2::«{mod3, mod4}ˇ»;
 5520
 5521                «ˇfn fn_1(param1: bool, param2: &str) {
 5522                    let var1 = "text";
 5523 5524            "#},
 5525            cx,
 5526        );
 5527    });
 5528
 5529    editor.update_in(cx, |editor, window, cx| {
 5530        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5531    });
 5532    editor.update(cx, |editor, cx| {
 5533        assert_text_with_selections(
 5534            editor,
 5535            indoc! {r#"
 5536                use mod1::mod2::{mod3, «mod4ˇ»};
 5537
 5538                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5539                    let var1 = "«textˇ»";
 5540                }
 5541            "#},
 5542            cx,
 5543        );
 5544    });
 5545
 5546    editor.update_in(cx, |editor, window, cx| {
 5547        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5548    });
 5549    editor.update(cx, |editor, cx| {
 5550        assert_text_with_selections(
 5551            editor,
 5552            indoc! {r#"
 5553                use mod1::mod2::{mod3, mo«ˇ»d4};
 5554
 5555                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5556                    let var1 = "te«ˇ»xt";
 5557                }
 5558            "#},
 5559            cx,
 5560        );
 5561    });
 5562
 5563    // Trying to shrink the selected syntax node one more time has no effect.
 5564    editor.update_in(cx, |editor, window, cx| {
 5565        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5566    });
 5567    editor.update_in(cx, |editor, _, cx| {
 5568        assert_text_with_selections(
 5569            editor,
 5570            indoc! {r#"
 5571                use mod1::mod2::{mod3, mo«ˇ»d4};
 5572
 5573                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5574                    let var1 = "te«ˇ»xt";
 5575                }
 5576            "#},
 5577            cx,
 5578        );
 5579    });
 5580
 5581    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5582    // a fold.
 5583    editor.update_in(cx, |editor, window, cx| {
 5584        editor.fold_creases(
 5585            vec![
 5586                Crease::simple(
 5587                    Point::new(0, 21)..Point::new(0, 24),
 5588                    FoldPlaceholder::test(),
 5589                ),
 5590                Crease::simple(
 5591                    Point::new(3, 20)..Point::new(3, 22),
 5592                    FoldPlaceholder::test(),
 5593                ),
 5594            ],
 5595            true,
 5596            window,
 5597            cx,
 5598        );
 5599        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5600    });
 5601    editor.update(cx, |editor, cx| {
 5602        assert_text_with_selections(
 5603            editor,
 5604            indoc! {r#"
 5605                use mod1::mod2::«{mod3, mod4}ˇ»;
 5606
 5607                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5608                    «let var1 = "text";ˇ»
 5609                }
 5610            "#},
 5611            cx,
 5612        );
 5613    });
 5614}
 5615
 5616#[gpui::test]
 5617async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5618    init_test(cx, |_| {});
 5619
 5620    let base_text = r#"
 5621        impl A {
 5622            // this is an uncommitted comment
 5623
 5624            fn b() {
 5625                c();
 5626            }
 5627
 5628            // this is another uncommitted comment
 5629
 5630            fn d() {
 5631                // e
 5632                // f
 5633            }
 5634        }
 5635
 5636        fn g() {
 5637            // h
 5638        }
 5639    "#
 5640    .unindent();
 5641
 5642    let text = r#"
 5643        ˇimpl A {
 5644
 5645            fn b() {
 5646                c();
 5647            }
 5648
 5649            fn d() {
 5650                // e
 5651                // f
 5652            }
 5653        }
 5654
 5655        fn g() {
 5656            // h
 5657        }
 5658    "#
 5659    .unindent();
 5660
 5661    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5662    cx.set_state(&text);
 5663    cx.set_diff_base(&base_text);
 5664    cx.update_editor(|editor, window, cx| {
 5665        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5666    });
 5667
 5668    cx.assert_state_with_diff(
 5669        "
 5670        ˇimpl A {
 5671      -     // this is an uncommitted comment
 5672
 5673            fn b() {
 5674                c();
 5675            }
 5676
 5677      -     // this is another uncommitted comment
 5678      -
 5679            fn d() {
 5680                // e
 5681                // f
 5682            }
 5683        }
 5684
 5685        fn g() {
 5686            // h
 5687        }
 5688    "
 5689        .unindent(),
 5690    );
 5691
 5692    let expected_display_text = "
 5693        impl A {
 5694            // this is an uncommitted comment
 5695
 5696            fn b() {
 5697 5698            }
 5699
 5700            // this is another uncommitted comment
 5701
 5702            fn d() {
 5703 5704            }
 5705        }
 5706
 5707        fn g() {
 5708 5709        }
 5710        "
 5711    .unindent();
 5712
 5713    cx.update_editor(|editor, window, cx| {
 5714        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5715        assert_eq!(editor.display_text(cx), expected_display_text);
 5716    });
 5717}
 5718
 5719#[gpui::test]
 5720async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5721    init_test(cx, |_| {});
 5722
 5723    let language = Arc::new(
 5724        Language::new(
 5725            LanguageConfig {
 5726                brackets: BracketPairConfig {
 5727                    pairs: vec![
 5728                        BracketPair {
 5729                            start: "{".to_string(),
 5730                            end: "}".to_string(),
 5731                            close: false,
 5732                            surround: false,
 5733                            newline: true,
 5734                        },
 5735                        BracketPair {
 5736                            start: "(".to_string(),
 5737                            end: ")".to_string(),
 5738                            close: false,
 5739                            surround: false,
 5740                            newline: true,
 5741                        },
 5742                    ],
 5743                    ..Default::default()
 5744                },
 5745                ..Default::default()
 5746            },
 5747            Some(tree_sitter_rust::LANGUAGE.into()),
 5748        )
 5749        .with_indents_query(
 5750            r#"
 5751                (_ "(" ")" @end) @indent
 5752                (_ "{" "}" @end) @indent
 5753            "#,
 5754        )
 5755        .unwrap(),
 5756    );
 5757
 5758    let text = "fn a() {}";
 5759
 5760    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5761    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5762    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5763    editor
 5764        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5765        .await;
 5766
 5767    editor.update_in(cx, |editor, window, cx| {
 5768        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5769        editor.newline(&Newline, window, cx);
 5770        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5771        assert_eq!(
 5772            editor.selections.ranges(cx),
 5773            &[
 5774                Point::new(1, 4)..Point::new(1, 4),
 5775                Point::new(3, 4)..Point::new(3, 4),
 5776                Point::new(5, 0)..Point::new(5, 0)
 5777            ]
 5778        );
 5779    });
 5780}
 5781
 5782#[gpui::test]
 5783async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5784    init_test(cx, |_| {});
 5785
 5786    {
 5787        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5788        cx.set_state(indoc! {"
 5789            impl A {
 5790
 5791                fn b() {}
 5792
 5793            «fn c() {
 5794
 5795            }ˇ»
 5796            }
 5797        "});
 5798
 5799        cx.update_editor(|editor, window, cx| {
 5800            editor.autoindent(&Default::default(), window, cx);
 5801        });
 5802
 5803        cx.assert_editor_state(indoc! {"
 5804            impl A {
 5805
 5806                fn b() {}
 5807
 5808                «fn c() {
 5809
 5810                }ˇ»
 5811            }
 5812        "});
 5813    }
 5814
 5815    {
 5816        let mut cx = EditorTestContext::new_multibuffer(
 5817            cx,
 5818            [indoc! { "
 5819                impl A {
 5820                «
 5821                // a
 5822                fn b(){}
 5823                »
 5824                «
 5825                    }
 5826                    fn c(){}
 5827                »
 5828            "}],
 5829        );
 5830
 5831        let buffer = cx.update_editor(|editor, _, cx| {
 5832            let buffer = editor.buffer().update(cx, |buffer, _| {
 5833                buffer.all_buffers().iter().next().unwrap().clone()
 5834            });
 5835            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5836            buffer
 5837        });
 5838
 5839        cx.run_until_parked();
 5840        cx.update_editor(|editor, window, cx| {
 5841            editor.select_all(&Default::default(), window, cx);
 5842            editor.autoindent(&Default::default(), window, cx)
 5843        });
 5844        cx.run_until_parked();
 5845
 5846        cx.update(|_, cx| {
 5847            pretty_assertions::assert_eq!(
 5848                buffer.read(cx).text(),
 5849                indoc! { "
 5850                    impl A {
 5851
 5852                        // a
 5853                        fn b(){}
 5854
 5855
 5856                    }
 5857                    fn c(){}
 5858
 5859                " }
 5860            )
 5861        });
 5862    }
 5863}
 5864
 5865#[gpui::test]
 5866async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5867    init_test(cx, |_| {});
 5868
 5869    let mut cx = EditorTestContext::new(cx).await;
 5870
 5871    let language = Arc::new(Language::new(
 5872        LanguageConfig {
 5873            brackets: BracketPairConfig {
 5874                pairs: vec![
 5875                    BracketPair {
 5876                        start: "{".to_string(),
 5877                        end: "}".to_string(),
 5878                        close: true,
 5879                        surround: true,
 5880                        newline: true,
 5881                    },
 5882                    BracketPair {
 5883                        start: "(".to_string(),
 5884                        end: ")".to_string(),
 5885                        close: true,
 5886                        surround: true,
 5887                        newline: true,
 5888                    },
 5889                    BracketPair {
 5890                        start: "/*".to_string(),
 5891                        end: " */".to_string(),
 5892                        close: true,
 5893                        surround: true,
 5894                        newline: true,
 5895                    },
 5896                    BracketPair {
 5897                        start: "[".to_string(),
 5898                        end: "]".to_string(),
 5899                        close: false,
 5900                        surround: false,
 5901                        newline: true,
 5902                    },
 5903                    BracketPair {
 5904                        start: "\"".to_string(),
 5905                        end: "\"".to_string(),
 5906                        close: true,
 5907                        surround: true,
 5908                        newline: false,
 5909                    },
 5910                    BracketPair {
 5911                        start: "<".to_string(),
 5912                        end: ">".to_string(),
 5913                        close: false,
 5914                        surround: true,
 5915                        newline: true,
 5916                    },
 5917                ],
 5918                ..Default::default()
 5919            },
 5920            autoclose_before: "})]".to_string(),
 5921            ..Default::default()
 5922        },
 5923        Some(tree_sitter_rust::LANGUAGE.into()),
 5924    ));
 5925
 5926    cx.language_registry().add(language.clone());
 5927    cx.update_buffer(|buffer, cx| {
 5928        buffer.set_language(Some(language), cx);
 5929    });
 5930
 5931    cx.set_state(
 5932        &r#"
 5933            🏀ˇ
 5934            εˇ
 5935            ❤️ˇ
 5936        "#
 5937        .unindent(),
 5938    );
 5939
 5940    // autoclose multiple nested brackets at multiple cursors
 5941    cx.update_editor(|editor, window, cx| {
 5942        editor.handle_input("{", window, cx);
 5943        editor.handle_input("{", window, cx);
 5944        editor.handle_input("{", window, cx);
 5945    });
 5946    cx.assert_editor_state(
 5947        &"
 5948            🏀{{{ˇ}}}
 5949            ε{{{ˇ}}}
 5950            ❤️{{{ˇ}}}
 5951        "
 5952        .unindent(),
 5953    );
 5954
 5955    // insert a different closing bracket
 5956    cx.update_editor(|editor, window, cx| {
 5957        editor.handle_input(")", window, cx);
 5958    });
 5959    cx.assert_editor_state(
 5960        &"
 5961            🏀{{{)ˇ}}}
 5962            ε{{{)ˇ}}}
 5963            ❤️{{{)ˇ}}}
 5964        "
 5965        .unindent(),
 5966    );
 5967
 5968    // skip over the auto-closed brackets when typing a closing bracket
 5969    cx.update_editor(|editor, window, cx| {
 5970        editor.move_right(&MoveRight, window, cx);
 5971        editor.handle_input("}", window, cx);
 5972        editor.handle_input("}", window, cx);
 5973        editor.handle_input("}", window, cx);
 5974    });
 5975    cx.assert_editor_state(
 5976        &"
 5977            🏀{{{)}}}}ˇ
 5978            ε{{{)}}}}ˇ
 5979            ❤️{{{)}}}}ˇ
 5980        "
 5981        .unindent(),
 5982    );
 5983
 5984    // autoclose multi-character pairs
 5985    cx.set_state(
 5986        &"
 5987            ˇ
 5988            ˇ
 5989        "
 5990        .unindent(),
 5991    );
 5992    cx.update_editor(|editor, window, cx| {
 5993        editor.handle_input("/", window, cx);
 5994        editor.handle_input("*", window, cx);
 5995    });
 5996    cx.assert_editor_state(
 5997        &"
 5998            /*ˇ */
 5999            /*ˇ */
 6000        "
 6001        .unindent(),
 6002    );
 6003
 6004    // one cursor autocloses a multi-character pair, one cursor
 6005    // does not autoclose.
 6006    cx.set_state(
 6007        &"
 6008 6009            ˇ
 6010        "
 6011        .unindent(),
 6012    );
 6013    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6014    cx.assert_editor_state(
 6015        &"
 6016            /*ˇ */
 6017 6018        "
 6019        .unindent(),
 6020    );
 6021
 6022    // Don't autoclose if the next character isn't whitespace and isn't
 6023    // listed in the language's "autoclose_before" section.
 6024    cx.set_state("ˇa b");
 6025    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6026    cx.assert_editor_state("{ˇa b");
 6027
 6028    // Don't autoclose if `close` is false for the bracket pair
 6029    cx.set_state("ˇ");
 6030    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6031    cx.assert_editor_state("");
 6032
 6033    // Surround with brackets if text is selected
 6034    cx.set_state("«aˇ» b");
 6035    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6036    cx.assert_editor_state("{«aˇ»} b");
 6037
 6038    // Autclose pair where the start and end characters are the same
 6039    cx.set_state("");
 6040    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6041    cx.assert_editor_state("a\"ˇ\"");
 6042    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6043    cx.assert_editor_state("a\"\"ˇ");
 6044
 6045    // Don't autoclose pair if autoclose is disabled
 6046    cx.set_state("ˇ");
 6047    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6048    cx.assert_editor_state("");
 6049
 6050    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6051    cx.set_state("«aˇ» b");
 6052    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6053    cx.assert_editor_state("<«aˇ»> b");
 6054}
 6055
 6056#[gpui::test]
 6057async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6058    init_test(cx, |settings| {
 6059        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6060    });
 6061
 6062    let mut cx = EditorTestContext::new(cx).await;
 6063
 6064    let language = Arc::new(Language::new(
 6065        LanguageConfig {
 6066            brackets: BracketPairConfig {
 6067                pairs: vec![
 6068                    BracketPair {
 6069                        start: "{".to_string(),
 6070                        end: "}".to_string(),
 6071                        close: true,
 6072                        surround: true,
 6073                        newline: true,
 6074                    },
 6075                    BracketPair {
 6076                        start: "(".to_string(),
 6077                        end: ")".to_string(),
 6078                        close: true,
 6079                        surround: true,
 6080                        newline: true,
 6081                    },
 6082                    BracketPair {
 6083                        start: "[".to_string(),
 6084                        end: "]".to_string(),
 6085                        close: false,
 6086                        surround: false,
 6087                        newline: true,
 6088                    },
 6089                ],
 6090                ..Default::default()
 6091            },
 6092            autoclose_before: "})]".to_string(),
 6093            ..Default::default()
 6094        },
 6095        Some(tree_sitter_rust::LANGUAGE.into()),
 6096    ));
 6097
 6098    cx.language_registry().add(language.clone());
 6099    cx.update_buffer(|buffer, cx| {
 6100        buffer.set_language(Some(language), cx);
 6101    });
 6102
 6103    cx.set_state(
 6104        &"
 6105            ˇ
 6106            ˇ
 6107            ˇ
 6108        "
 6109        .unindent(),
 6110    );
 6111
 6112    // ensure only matching closing brackets are skipped over
 6113    cx.update_editor(|editor, window, cx| {
 6114        editor.handle_input("}", window, cx);
 6115        editor.move_left(&MoveLeft, window, cx);
 6116        editor.handle_input(")", window, cx);
 6117        editor.move_left(&MoveLeft, window, cx);
 6118    });
 6119    cx.assert_editor_state(
 6120        &"
 6121            ˇ)}
 6122            ˇ)}
 6123            ˇ)}
 6124        "
 6125        .unindent(),
 6126    );
 6127
 6128    // skip-over closing brackets at multiple cursors
 6129    cx.update_editor(|editor, window, cx| {
 6130        editor.handle_input(")", window, cx);
 6131        editor.handle_input("}", window, cx);
 6132    });
 6133    cx.assert_editor_state(
 6134        &"
 6135            )}ˇ
 6136            )}ˇ
 6137            )}ˇ
 6138        "
 6139        .unindent(),
 6140    );
 6141
 6142    // ignore non-close brackets
 6143    cx.update_editor(|editor, window, cx| {
 6144        editor.handle_input("]", window, cx);
 6145        editor.move_left(&MoveLeft, window, cx);
 6146        editor.handle_input("]", window, cx);
 6147    });
 6148    cx.assert_editor_state(
 6149        &"
 6150            )}]ˇ]
 6151            )}]ˇ]
 6152            )}]ˇ]
 6153        "
 6154        .unindent(),
 6155    );
 6156}
 6157
 6158#[gpui::test]
 6159async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6160    init_test(cx, |_| {});
 6161
 6162    let mut cx = EditorTestContext::new(cx).await;
 6163
 6164    let html_language = Arc::new(
 6165        Language::new(
 6166            LanguageConfig {
 6167                name: "HTML".into(),
 6168                brackets: BracketPairConfig {
 6169                    pairs: vec![
 6170                        BracketPair {
 6171                            start: "<".into(),
 6172                            end: ">".into(),
 6173                            close: true,
 6174                            ..Default::default()
 6175                        },
 6176                        BracketPair {
 6177                            start: "{".into(),
 6178                            end: "}".into(),
 6179                            close: true,
 6180                            ..Default::default()
 6181                        },
 6182                        BracketPair {
 6183                            start: "(".into(),
 6184                            end: ")".into(),
 6185                            close: true,
 6186                            ..Default::default()
 6187                        },
 6188                    ],
 6189                    ..Default::default()
 6190                },
 6191                autoclose_before: "})]>".into(),
 6192                ..Default::default()
 6193            },
 6194            Some(tree_sitter_html::language()),
 6195        )
 6196        .with_injection_query(
 6197            r#"
 6198            (script_element
 6199                (raw_text) @injection.content
 6200                (#set! injection.language "javascript"))
 6201            "#,
 6202        )
 6203        .unwrap(),
 6204    );
 6205
 6206    let javascript_language = Arc::new(Language::new(
 6207        LanguageConfig {
 6208            name: "JavaScript".into(),
 6209            brackets: BracketPairConfig {
 6210                pairs: vec![
 6211                    BracketPair {
 6212                        start: "/*".into(),
 6213                        end: " */".into(),
 6214                        close: true,
 6215                        ..Default::default()
 6216                    },
 6217                    BracketPair {
 6218                        start: "{".into(),
 6219                        end: "}".into(),
 6220                        close: true,
 6221                        ..Default::default()
 6222                    },
 6223                    BracketPair {
 6224                        start: "(".into(),
 6225                        end: ")".into(),
 6226                        close: true,
 6227                        ..Default::default()
 6228                    },
 6229                ],
 6230                ..Default::default()
 6231            },
 6232            autoclose_before: "})]>".into(),
 6233            ..Default::default()
 6234        },
 6235        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6236    ));
 6237
 6238    cx.language_registry().add(html_language.clone());
 6239    cx.language_registry().add(javascript_language.clone());
 6240
 6241    cx.update_buffer(|buffer, cx| {
 6242        buffer.set_language(Some(html_language), cx);
 6243    });
 6244
 6245    cx.set_state(
 6246        &r#"
 6247            <body>ˇ
 6248                <script>
 6249                    var x = 1;ˇ
 6250                </script>
 6251            </body>ˇ
 6252        "#
 6253        .unindent(),
 6254    );
 6255
 6256    // Precondition: different languages are active at different locations.
 6257    cx.update_editor(|editor, window, cx| {
 6258        let snapshot = editor.snapshot(window, cx);
 6259        let cursors = editor.selections.ranges::<usize>(cx);
 6260        let languages = cursors
 6261            .iter()
 6262            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6263            .collect::<Vec<_>>();
 6264        assert_eq!(
 6265            languages,
 6266            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6267        );
 6268    });
 6269
 6270    // Angle brackets autoclose in HTML, but not JavaScript.
 6271    cx.update_editor(|editor, window, cx| {
 6272        editor.handle_input("<", window, cx);
 6273        editor.handle_input("a", window, cx);
 6274    });
 6275    cx.assert_editor_state(
 6276        &r#"
 6277            <body><aˇ>
 6278                <script>
 6279                    var x = 1;<aˇ
 6280                </script>
 6281            </body><aˇ>
 6282        "#
 6283        .unindent(),
 6284    );
 6285
 6286    // Curly braces and parens autoclose in both HTML and JavaScript.
 6287    cx.update_editor(|editor, window, cx| {
 6288        editor.handle_input(" b=", window, cx);
 6289        editor.handle_input("{", window, cx);
 6290        editor.handle_input("c", window, cx);
 6291        editor.handle_input("(", window, cx);
 6292    });
 6293    cx.assert_editor_state(
 6294        &r#"
 6295            <body><a b={c(ˇ)}>
 6296                <script>
 6297                    var x = 1;<a b={c(ˇ)}
 6298                </script>
 6299            </body><a b={c(ˇ)}>
 6300        "#
 6301        .unindent(),
 6302    );
 6303
 6304    // Brackets that were already autoclosed are skipped.
 6305    cx.update_editor(|editor, window, cx| {
 6306        editor.handle_input(")", window, cx);
 6307        editor.handle_input("d", window, cx);
 6308        editor.handle_input("}", window, cx);
 6309    });
 6310    cx.assert_editor_state(
 6311        &r#"
 6312            <body><a b={c()d}ˇ>
 6313                <script>
 6314                    var x = 1;<a b={c()d}ˇ
 6315                </script>
 6316            </body><a b={c()d}ˇ>
 6317        "#
 6318        .unindent(),
 6319    );
 6320    cx.update_editor(|editor, window, cx| {
 6321        editor.handle_input(">", window, cx);
 6322    });
 6323    cx.assert_editor_state(
 6324        &r#"
 6325            <body><a b={c()d}>ˇ
 6326                <script>
 6327                    var x = 1;<a b={c()d}>ˇ
 6328                </script>
 6329            </body><a b={c()d}>ˇ
 6330        "#
 6331        .unindent(),
 6332    );
 6333
 6334    // Reset
 6335    cx.set_state(
 6336        &r#"
 6337            <body>ˇ
 6338                <script>
 6339                    var x = 1;ˇ
 6340                </script>
 6341            </body>ˇ
 6342        "#
 6343        .unindent(),
 6344    );
 6345
 6346    cx.update_editor(|editor, window, cx| {
 6347        editor.handle_input("<", window, cx);
 6348    });
 6349    cx.assert_editor_state(
 6350        &r#"
 6351            <body><ˇ>
 6352                <script>
 6353                    var x = 1;<ˇ
 6354                </script>
 6355            </body><ˇ>
 6356        "#
 6357        .unindent(),
 6358    );
 6359
 6360    // When backspacing, the closing angle brackets are removed.
 6361    cx.update_editor(|editor, window, cx| {
 6362        editor.backspace(&Backspace, window, cx);
 6363    });
 6364    cx.assert_editor_state(
 6365        &r#"
 6366            <body>ˇ
 6367                <script>
 6368                    var x = 1;ˇ
 6369                </script>
 6370            </body>ˇ
 6371        "#
 6372        .unindent(),
 6373    );
 6374
 6375    // Block comments autoclose in JavaScript, but not HTML.
 6376    cx.update_editor(|editor, window, cx| {
 6377        editor.handle_input("/", window, cx);
 6378        editor.handle_input("*", window, cx);
 6379    });
 6380    cx.assert_editor_state(
 6381        &r#"
 6382            <body>/*ˇ
 6383                <script>
 6384                    var x = 1;/*ˇ */
 6385                </script>
 6386            </body>/*ˇ
 6387        "#
 6388        .unindent(),
 6389    );
 6390}
 6391
 6392#[gpui::test]
 6393async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6394    init_test(cx, |_| {});
 6395
 6396    let mut cx = EditorTestContext::new(cx).await;
 6397
 6398    let rust_language = Arc::new(
 6399        Language::new(
 6400            LanguageConfig {
 6401                name: "Rust".into(),
 6402                brackets: serde_json::from_value(json!([
 6403                    { "start": "{", "end": "}", "close": true, "newline": true },
 6404                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6405                ]))
 6406                .unwrap(),
 6407                autoclose_before: "})]>".into(),
 6408                ..Default::default()
 6409            },
 6410            Some(tree_sitter_rust::LANGUAGE.into()),
 6411        )
 6412        .with_override_query("(string_literal) @string")
 6413        .unwrap(),
 6414    );
 6415
 6416    cx.language_registry().add(rust_language.clone());
 6417    cx.update_buffer(|buffer, cx| {
 6418        buffer.set_language(Some(rust_language), cx);
 6419    });
 6420
 6421    cx.set_state(
 6422        &r#"
 6423            let x = ˇ
 6424        "#
 6425        .unindent(),
 6426    );
 6427
 6428    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6429    cx.update_editor(|editor, window, cx| {
 6430        editor.handle_input("\"", window, cx);
 6431    });
 6432    cx.assert_editor_state(
 6433        &r#"
 6434            let x = "ˇ"
 6435        "#
 6436        .unindent(),
 6437    );
 6438
 6439    // Inserting another quotation mark. The cursor moves across the existing
 6440    // automatically-inserted quotation mark.
 6441    cx.update_editor(|editor, window, cx| {
 6442        editor.handle_input("\"", window, cx);
 6443    });
 6444    cx.assert_editor_state(
 6445        &r#"
 6446            let x = ""ˇ
 6447        "#
 6448        .unindent(),
 6449    );
 6450
 6451    // Reset
 6452    cx.set_state(
 6453        &r#"
 6454            let x = ˇ
 6455        "#
 6456        .unindent(),
 6457    );
 6458
 6459    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6460    cx.update_editor(|editor, window, cx| {
 6461        editor.handle_input("\"", window, cx);
 6462        editor.handle_input(" ", window, cx);
 6463        editor.move_left(&Default::default(), window, cx);
 6464        editor.handle_input("\\", window, cx);
 6465        editor.handle_input("\"", window, cx);
 6466    });
 6467    cx.assert_editor_state(
 6468        &r#"
 6469            let x = "\"ˇ "
 6470        "#
 6471        .unindent(),
 6472    );
 6473
 6474    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6475    // mark. Nothing is inserted.
 6476    cx.update_editor(|editor, window, cx| {
 6477        editor.move_right(&Default::default(), window, cx);
 6478        editor.handle_input("\"", window, cx);
 6479    });
 6480    cx.assert_editor_state(
 6481        &r#"
 6482            let x = "\" "ˇ
 6483        "#
 6484        .unindent(),
 6485    );
 6486}
 6487
 6488#[gpui::test]
 6489async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6490    init_test(cx, |_| {});
 6491
 6492    let language = Arc::new(Language::new(
 6493        LanguageConfig {
 6494            brackets: BracketPairConfig {
 6495                pairs: vec![
 6496                    BracketPair {
 6497                        start: "{".to_string(),
 6498                        end: "}".to_string(),
 6499                        close: true,
 6500                        surround: true,
 6501                        newline: true,
 6502                    },
 6503                    BracketPair {
 6504                        start: "/* ".to_string(),
 6505                        end: "*/".to_string(),
 6506                        close: true,
 6507                        surround: true,
 6508                        ..Default::default()
 6509                    },
 6510                ],
 6511                ..Default::default()
 6512            },
 6513            ..Default::default()
 6514        },
 6515        Some(tree_sitter_rust::LANGUAGE.into()),
 6516    ));
 6517
 6518    let text = r#"
 6519        a
 6520        b
 6521        c
 6522    "#
 6523    .unindent();
 6524
 6525    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6526    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6527    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6528    editor
 6529        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6530        .await;
 6531
 6532    editor.update_in(cx, |editor, window, cx| {
 6533        editor.change_selections(None, window, cx, |s| {
 6534            s.select_display_ranges([
 6535                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6536                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6537                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6538            ])
 6539        });
 6540
 6541        editor.handle_input("{", window, cx);
 6542        editor.handle_input("{", window, cx);
 6543        editor.handle_input("{", window, cx);
 6544        assert_eq!(
 6545            editor.text(cx),
 6546            "
 6547                {{{a}}}
 6548                {{{b}}}
 6549                {{{c}}}
 6550            "
 6551            .unindent()
 6552        );
 6553        assert_eq!(
 6554            editor.selections.display_ranges(cx),
 6555            [
 6556                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6557                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6558                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6559            ]
 6560        );
 6561
 6562        editor.undo(&Undo, window, cx);
 6563        editor.undo(&Undo, window, cx);
 6564        editor.undo(&Undo, window, cx);
 6565        assert_eq!(
 6566            editor.text(cx),
 6567            "
 6568                a
 6569                b
 6570                c
 6571            "
 6572            .unindent()
 6573        );
 6574        assert_eq!(
 6575            editor.selections.display_ranges(cx),
 6576            [
 6577                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6578                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6579                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6580            ]
 6581        );
 6582
 6583        // Ensure inserting the first character of a multi-byte bracket pair
 6584        // doesn't surround the selections with the bracket.
 6585        editor.handle_input("/", window, cx);
 6586        assert_eq!(
 6587            editor.text(cx),
 6588            "
 6589                /
 6590                /
 6591                /
 6592            "
 6593            .unindent()
 6594        );
 6595        assert_eq!(
 6596            editor.selections.display_ranges(cx),
 6597            [
 6598                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6599                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6600                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6601            ]
 6602        );
 6603
 6604        editor.undo(&Undo, window, cx);
 6605        assert_eq!(
 6606            editor.text(cx),
 6607            "
 6608                a
 6609                b
 6610                c
 6611            "
 6612            .unindent()
 6613        );
 6614        assert_eq!(
 6615            editor.selections.display_ranges(cx),
 6616            [
 6617                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6618                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6619                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6620            ]
 6621        );
 6622
 6623        // Ensure inserting the last character of a multi-byte bracket pair
 6624        // doesn't surround the selections with the bracket.
 6625        editor.handle_input("*", window, cx);
 6626        assert_eq!(
 6627            editor.text(cx),
 6628            "
 6629                *
 6630                *
 6631                *
 6632            "
 6633            .unindent()
 6634        );
 6635        assert_eq!(
 6636            editor.selections.display_ranges(cx),
 6637            [
 6638                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6639                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6640                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6641            ]
 6642        );
 6643    });
 6644}
 6645
 6646#[gpui::test]
 6647async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6648    init_test(cx, |_| {});
 6649
 6650    let language = Arc::new(Language::new(
 6651        LanguageConfig {
 6652            brackets: BracketPairConfig {
 6653                pairs: vec![BracketPair {
 6654                    start: "{".to_string(),
 6655                    end: "}".to_string(),
 6656                    close: true,
 6657                    surround: true,
 6658                    newline: true,
 6659                }],
 6660                ..Default::default()
 6661            },
 6662            autoclose_before: "}".to_string(),
 6663            ..Default::default()
 6664        },
 6665        Some(tree_sitter_rust::LANGUAGE.into()),
 6666    ));
 6667
 6668    let text = r#"
 6669        a
 6670        b
 6671        c
 6672    "#
 6673    .unindent();
 6674
 6675    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6676    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6677    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6678    editor
 6679        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6680        .await;
 6681
 6682    editor.update_in(cx, |editor, window, cx| {
 6683        editor.change_selections(None, window, cx, |s| {
 6684            s.select_ranges([
 6685                Point::new(0, 1)..Point::new(0, 1),
 6686                Point::new(1, 1)..Point::new(1, 1),
 6687                Point::new(2, 1)..Point::new(2, 1),
 6688            ])
 6689        });
 6690
 6691        editor.handle_input("{", window, cx);
 6692        editor.handle_input("{", window, cx);
 6693        editor.handle_input("_", window, cx);
 6694        assert_eq!(
 6695            editor.text(cx),
 6696            "
 6697                a{{_}}
 6698                b{{_}}
 6699                c{{_}}
 6700            "
 6701            .unindent()
 6702        );
 6703        assert_eq!(
 6704            editor.selections.ranges::<Point>(cx),
 6705            [
 6706                Point::new(0, 4)..Point::new(0, 4),
 6707                Point::new(1, 4)..Point::new(1, 4),
 6708                Point::new(2, 4)..Point::new(2, 4)
 6709            ]
 6710        );
 6711
 6712        editor.backspace(&Default::default(), window, cx);
 6713        editor.backspace(&Default::default(), window, cx);
 6714        assert_eq!(
 6715            editor.text(cx),
 6716            "
 6717                a{}
 6718                b{}
 6719                c{}
 6720            "
 6721            .unindent()
 6722        );
 6723        assert_eq!(
 6724            editor.selections.ranges::<Point>(cx),
 6725            [
 6726                Point::new(0, 2)..Point::new(0, 2),
 6727                Point::new(1, 2)..Point::new(1, 2),
 6728                Point::new(2, 2)..Point::new(2, 2)
 6729            ]
 6730        );
 6731
 6732        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6733        assert_eq!(
 6734            editor.text(cx),
 6735            "
 6736                a
 6737                b
 6738                c
 6739            "
 6740            .unindent()
 6741        );
 6742        assert_eq!(
 6743            editor.selections.ranges::<Point>(cx),
 6744            [
 6745                Point::new(0, 1)..Point::new(0, 1),
 6746                Point::new(1, 1)..Point::new(1, 1),
 6747                Point::new(2, 1)..Point::new(2, 1)
 6748            ]
 6749        );
 6750    });
 6751}
 6752
 6753#[gpui::test]
 6754async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6755    init_test(cx, |settings| {
 6756        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6757    });
 6758
 6759    let mut cx = EditorTestContext::new(cx).await;
 6760
 6761    let language = Arc::new(Language::new(
 6762        LanguageConfig {
 6763            brackets: BracketPairConfig {
 6764                pairs: vec![
 6765                    BracketPair {
 6766                        start: "{".to_string(),
 6767                        end: "}".to_string(),
 6768                        close: true,
 6769                        surround: true,
 6770                        newline: true,
 6771                    },
 6772                    BracketPair {
 6773                        start: "(".to_string(),
 6774                        end: ")".to_string(),
 6775                        close: true,
 6776                        surround: true,
 6777                        newline: true,
 6778                    },
 6779                    BracketPair {
 6780                        start: "[".to_string(),
 6781                        end: "]".to_string(),
 6782                        close: false,
 6783                        surround: true,
 6784                        newline: true,
 6785                    },
 6786                ],
 6787                ..Default::default()
 6788            },
 6789            autoclose_before: "})]".to_string(),
 6790            ..Default::default()
 6791        },
 6792        Some(tree_sitter_rust::LANGUAGE.into()),
 6793    ));
 6794
 6795    cx.language_registry().add(language.clone());
 6796    cx.update_buffer(|buffer, cx| {
 6797        buffer.set_language(Some(language), cx);
 6798    });
 6799
 6800    cx.set_state(
 6801        &"
 6802            {(ˇ)}
 6803            [[ˇ]]
 6804            {(ˇ)}
 6805        "
 6806        .unindent(),
 6807    );
 6808
 6809    cx.update_editor(|editor, window, cx| {
 6810        editor.backspace(&Default::default(), window, cx);
 6811        editor.backspace(&Default::default(), window, cx);
 6812    });
 6813
 6814    cx.assert_editor_state(
 6815        &"
 6816            ˇ
 6817            ˇ]]
 6818            ˇ
 6819        "
 6820        .unindent(),
 6821    );
 6822
 6823    cx.update_editor(|editor, window, cx| {
 6824        editor.handle_input("{", window, cx);
 6825        editor.handle_input("{", window, cx);
 6826        editor.move_right(&MoveRight, window, cx);
 6827        editor.move_right(&MoveRight, window, cx);
 6828        editor.move_left(&MoveLeft, window, cx);
 6829        editor.move_left(&MoveLeft, window, cx);
 6830        editor.backspace(&Default::default(), window, cx);
 6831    });
 6832
 6833    cx.assert_editor_state(
 6834        &"
 6835            {ˇ}
 6836            {ˇ}]]
 6837            {ˇ}
 6838        "
 6839        .unindent(),
 6840    );
 6841
 6842    cx.update_editor(|editor, window, cx| {
 6843        editor.backspace(&Default::default(), window, cx);
 6844    });
 6845
 6846    cx.assert_editor_state(
 6847        &"
 6848            ˇ
 6849            ˇ]]
 6850            ˇ
 6851        "
 6852        .unindent(),
 6853    );
 6854}
 6855
 6856#[gpui::test]
 6857async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6858    init_test(cx, |_| {});
 6859
 6860    let language = Arc::new(Language::new(
 6861        LanguageConfig::default(),
 6862        Some(tree_sitter_rust::LANGUAGE.into()),
 6863    ));
 6864
 6865    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6866    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6867    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6868    editor
 6869        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6870        .await;
 6871
 6872    editor.update_in(cx, |editor, window, cx| {
 6873        editor.set_auto_replace_emoji_shortcode(true);
 6874
 6875        editor.handle_input("Hello ", window, cx);
 6876        editor.handle_input(":wave", window, cx);
 6877        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6878
 6879        editor.handle_input(":", window, cx);
 6880        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6881
 6882        editor.handle_input(" :smile", window, cx);
 6883        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6884
 6885        editor.handle_input(":", window, cx);
 6886        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6887
 6888        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6889        editor.handle_input(":wave", window, cx);
 6890        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6891
 6892        editor.handle_input(":", window, cx);
 6893        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6894
 6895        editor.handle_input(":1", window, cx);
 6896        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6897
 6898        editor.handle_input(":", window, cx);
 6899        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6900
 6901        // Ensure shortcode does not get replaced when it is part of a word
 6902        editor.handle_input(" Test:wave", window, cx);
 6903        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6904
 6905        editor.handle_input(":", window, cx);
 6906        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6907
 6908        editor.set_auto_replace_emoji_shortcode(false);
 6909
 6910        // Ensure shortcode does not get replaced when auto replace is off
 6911        editor.handle_input(" :wave", window, cx);
 6912        assert_eq!(
 6913            editor.text(cx),
 6914            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6915        );
 6916
 6917        editor.handle_input(":", window, cx);
 6918        assert_eq!(
 6919            editor.text(cx),
 6920            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6921        );
 6922    });
 6923}
 6924
 6925#[gpui::test]
 6926async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6927    init_test(cx, |_| {});
 6928
 6929    let (text, insertion_ranges) = marked_text_ranges(
 6930        indoc! {"
 6931            ˇ
 6932        "},
 6933        false,
 6934    );
 6935
 6936    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6937    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6938
 6939    _ = editor.update_in(cx, |editor, window, cx| {
 6940        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6941
 6942        editor
 6943            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6944            .unwrap();
 6945
 6946        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6947            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6948            assert_eq!(editor.text(cx), expected_text);
 6949            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6950        }
 6951
 6952        assert(
 6953            editor,
 6954            cx,
 6955            indoc! {"
 6956            type «» =•
 6957            "},
 6958        );
 6959
 6960        assert!(editor.context_menu_visible(), "There should be a matches");
 6961    });
 6962}
 6963
 6964#[gpui::test]
 6965async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6966    init_test(cx, |_| {});
 6967
 6968    let (text, insertion_ranges) = marked_text_ranges(
 6969        indoc! {"
 6970            a.ˇ b
 6971            a.ˇ b
 6972            a.ˇ b
 6973        "},
 6974        false,
 6975    );
 6976
 6977    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6978    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6979
 6980    editor.update_in(cx, |editor, window, cx| {
 6981        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6982
 6983        editor
 6984            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6985            .unwrap();
 6986
 6987        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6988            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6989            assert_eq!(editor.text(cx), expected_text);
 6990            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6991        }
 6992
 6993        assert(
 6994            editor,
 6995            cx,
 6996            indoc! {"
 6997                a.f(«one», two, «three») b
 6998                a.f(«one», two, «three») b
 6999                a.f(«one», two, «three») b
 7000            "},
 7001        );
 7002
 7003        // Can't move earlier than the first tab stop
 7004        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7005        assert(
 7006            editor,
 7007            cx,
 7008            indoc! {"
 7009                a.f(«one», two, «three») b
 7010                a.f(«one», two, «three») b
 7011                a.f(«one», two, «three») b
 7012            "},
 7013        );
 7014
 7015        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7016        assert(
 7017            editor,
 7018            cx,
 7019            indoc! {"
 7020                a.f(one, «two», three) b
 7021                a.f(one, «two», three) b
 7022                a.f(one, «two», three) b
 7023            "},
 7024        );
 7025
 7026        editor.move_to_prev_snippet_tabstop(window, cx);
 7027        assert(
 7028            editor,
 7029            cx,
 7030            indoc! {"
 7031                a.f(«one», two, «three») b
 7032                a.f(«one», two, «three») b
 7033                a.f(«one», two, «three») b
 7034            "},
 7035        );
 7036
 7037        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7038        assert(
 7039            editor,
 7040            cx,
 7041            indoc! {"
 7042                a.f(one, «two», three) b
 7043                a.f(one, «two», three) b
 7044                a.f(one, «two», three) b
 7045            "},
 7046        );
 7047        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7048        assert(
 7049            editor,
 7050            cx,
 7051            indoc! {"
 7052                a.f(one, two, three)ˇ b
 7053                a.f(one, two, three)ˇ b
 7054                a.f(one, two, three)ˇ b
 7055            "},
 7056        );
 7057
 7058        // As soon as the last tab stop is reached, snippet state is gone
 7059        editor.move_to_prev_snippet_tabstop(window, cx);
 7060        assert(
 7061            editor,
 7062            cx,
 7063            indoc! {"
 7064                a.f(one, two, three)ˇ b
 7065                a.f(one, two, three)ˇ b
 7066                a.f(one, two, three)ˇ b
 7067            "},
 7068        );
 7069    });
 7070}
 7071
 7072#[gpui::test]
 7073async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7074    init_test(cx, |_| {});
 7075
 7076    let fs = FakeFs::new(cx.executor());
 7077    fs.insert_file("/file.rs", Default::default()).await;
 7078
 7079    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7080
 7081    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7082    language_registry.add(rust_lang());
 7083    let mut fake_servers = language_registry.register_fake_lsp(
 7084        "Rust",
 7085        FakeLspAdapter {
 7086            capabilities: lsp::ServerCapabilities {
 7087                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7088                ..Default::default()
 7089            },
 7090            ..Default::default()
 7091        },
 7092    );
 7093
 7094    let buffer = project
 7095        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7096        .await
 7097        .unwrap();
 7098
 7099    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7100    let (editor, cx) = cx.add_window_view(|window, cx| {
 7101        build_editor_with_project(project.clone(), buffer, window, cx)
 7102    });
 7103    editor.update_in(cx, |editor, window, cx| {
 7104        editor.set_text("one\ntwo\nthree\n", window, cx)
 7105    });
 7106    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7107
 7108    cx.executor().start_waiting();
 7109    let fake_server = fake_servers.next().await.unwrap();
 7110
 7111    let save = editor
 7112        .update_in(cx, |editor, window, cx| {
 7113            editor.save(true, project.clone(), window, cx)
 7114        })
 7115        .unwrap();
 7116    fake_server
 7117        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7118            assert_eq!(
 7119                params.text_document.uri,
 7120                lsp::Url::from_file_path("/file.rs").unwrap()
 7121            );
 7122            assert_eq!(params.options.tab_size, 4);
 7123            Ok(Some(vec![lsp::TextEdit::new(
 7124                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7125                ", ".to_string(),
 7126            )]))
 7127        })
 7128        .next()
 7129        .await;
 7130    cx.executor().start_waiting();
 7131    save.await;
 7132
 7133    assert_eq!(
 7134        editor.update(cx, |editor, cx| editor.text(cx)),
 7135        "one, two\nthree\n"
 7136    );
 7137    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7138
 7139    editor.update_in(cx, |editor, window, cx| {
 7140        editor.set_text("one\ntwo\nthree\n", window, cx)
 7141    });
 7142    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7143
 7144    // Ensure we can still save even if formatting hangs.
 7145    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7146        assert_eq!(
 7147            params.text_document.uri,
 7148            lsp::Url::from_file_path("/file.rs").unwrap()
 7149        );
 7150        futures::future::pending::<()>().await;
 7151        unreachable!()
 7152    });
 7153    let save = editor
 7154        .update_in(cx, |editor, window, cx| {
 7155            editor.save(true, project.clone(), window, cx)
 7156        })
 7157        .unwrap();
 7158    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7159    cx.executor().start_waiting();
 7160    save.await;
 7161    assert_eq!(
 7162        editor.update(cx, |editor, cx| editor.text(cx)),
 7163        "one\ntwo\nthree\n"
 7164    );
 7165    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7166
 7167    // For non-dirty buffer, no formatting request should be sent
 7168    let save = editor
 7169        .update_in(cx, |editor, window, cx| {
 7170            editor.save(true, project.clone(), window, cx)
 7171        })
 7172        .unwrap();
 7173    let _pending_format_request = fake_server
 7174        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7175            panic!("Should not be invoked on non-dirty buffer");
 7176        })
 7177        .next();
 7178    cx.executor().start_waiting();
 7179    save.await;
 7180
 7181    // Set rust language override and assert overridden tabsize is sent to language server
 7182    update_test_language_settings(cx, |settings| {
 7183        settings.languages.insert(
 7184            "Rust".into(),
 7185            LanguageSettingsContent {
 7186                tab_size: NonZeroU32::new(8),
 7187                ..Default::default()
 7188            },
 7189        );
 7190    });
 7191
 7192    editor.update_in(cx, |editor, window, cx| {
 7193        editor.set_text("somehting_new\n", window, cx)
 7194    });
 7195    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7196    let save = editor
 7197        .update_in(cx, |editor, window, cx| {
 7198            editor.save(true, project.clone(), window, cx)
 7199        })
 7200        .unwrap();
 7201    fake_server
 7202        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7203            assert_eq!(
 7204                params.text_document.uri,
 7205                lsp::Url::from_file_path("/file.rs").unwrap()
 7206            );
 7207            assert_eq!(params.options.tab_size, 8);
 7208            Ok(Some(vec![]))
 7209        })
 7210        .next()
 7211        .await;
 7212    cx.executor().start_waiting();
 7213    save.await;
 7214}
 7215
 7216#[gpui::test]
 7217async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7218    init_test(cx, |_| {});
 7219
 7220    let cols = 4;
 7221    let rows = 10;
 7222    let sample_text_1 = sample_text(rows, cols, 'a');
 7223    assert_eq!(
 7224        sample_text_1,
 7225        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7226    );
 7227    let sample_text_2 = sample_text(rows, cols, 'l');
 7228    assert_eq!(
 7229        sample_text_2,
 7230        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7231    );
 7232    let sample_text_3 = sample_text(rows, cols, 'v');
 7233    assert_eq!(
 7234        sample_text_3,
 7235        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7236    );
 7237
 7238    let fs = FakeFs::new(cx.executor());
 7239    fs.insert_tree(
 7240        "/a",
 7241        json!({
 7242            "main.rs": sample_text_1,
 7243            "other.rs": sample_text_2,
 7244            "lib.rs": sample_text_3,
 7245        }),
 7246    )
 7247    .await;
 7248
 7249    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 7250    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7251    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7252
 7253    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7254    language_registry.add(rust_lang());
 7255    let mut fake_servers = language_registry.register_fake_lsp(
 7256        "Rust",
 7257        FakeLspAdapter {
 7258            capabilities: lsp::ServerCapabilities {
 7259                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7260                ..Default::default()
 7261            },
 7262            ..Default::default()
 7263        },
 7264    );
 7265
 7266    let worktree = project.update(cx, |project, cx| {
 7267        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7268        assert_eq!(worktrees.len(), 1);
 7269        worktrees.pop().unwrap()
 7270    });
 7271    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7272
 7273    let buffer_1 = project
 7274        .update(cx, |project, cx| {
 7275            project.open_buffer((worktree_id, "main.rs"), cx)
 7276        })
 7277        .await
 7278        .unwrap();
 7279    let buffer_2 = project
 7280        .update(cx, |project, cx| {
 7281            project.open_buffer((worktree_id, "other.rs"), cx)
 7282        })
 7283        .await
 7284        .unwrap();
 7285    let buffer_3 = project
 7286        .update(cx, |project, cx| {
 7287            project.open_buffer((worktree_id, "lib.rs"), cx)
 7288        })
 7289        .await
 7290        .unwrap();
 7291
 7292    let multi_buffer = cx.new(|cx| {
 7293        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7294        multi_buffer.push_excerpts(
 7295            buffer_1.clone(),
 7296            [
 7297                ExcerptRange {
 7298                    context: Point::new(0, 0)..Point::new(3, 0),
 7299                    primary: None,
 7300                },
 7301                ExcerptRange {
 7302                    context: Point::new(5, 0)..Point::new(7, 0),
 7303                    primary: None,
 7304                },
 7305                ExcerptRange {
 7306                    context: Point::new(9, 0)..Point::new(10, 4),
 7307                    primary: None,
 7308                },
 7309            ],
 7310            cx,
 7311        );
 7312        multi_buffer.push_excerpts(
 7313            buffer_2.clone(),
 7314            [
 7315                ExcerptRange {
 7316                    context: Point::new(0, 0)..Point::new(3, 0),
 7317                    primary: None,
 7318                },
 7319                ExcerptRange {
 7320                    context: Point::new(5, 0)..Point::new(7, 0),
 7321                    primary: None,
 7322                },
 7323                ExcerptRange {
 7324                    context: Point::new(9, 0)..Point::new(10, 4),
 7325                    primary: None,
 7326                },
 7327            ],
 7328            cx,
 7329        );
 7330        multi_buffer.push_excerpts(
 7331            buffer_3.clone(),
 7332            [
 7333                ExcerptRange {
 7334                    context: Point::new(0, 0)..Point::new(3, 0),
 7335                    primary: None,
 7336                },
 7337                ExcerptRange {
 7338                    context: Point::new(5, 0)..Point::new(7, 0),
 7339                    primary: None,
 7340                },
 7341                ExcerptRange {
 7342                    context: Point::new(9, 0)..Point::new(10, 4),
 7343                    primary: None,
 7344                },
 7345            ],
 7346            cx,
 7347        );
 7348        multi_buffer
 7349    });
 7350    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7351        Editor::new(
 7352            EditorMode::Full,
 7353            multi_buffer,
 7354            Some(project.clone()),
 7355            true,
 7356            window,
 7357            cx,
 7358        )
 7359    });
 7360
 7361    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7362        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7363            s.select_ranges(Some(1..2))
 7364        });
 7365        editor.insert("|one|two|three|", window, cx);
 7366    });
 7367    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7368    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7369        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7370            s.select_ranges(Some(60..70))
 7371        });
 7372        editor.insert("|four|five|six|", window, cx);
 7373    });
 7374    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7375
 7376    // First two buffers should be edited, but not the third one.
 7377    assert_eq!(
 7378        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7379        "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}",
 7380    );
 7381    buffer_1.update(cx, |buffer, _| {
 7382        assert!(buffer.is_dirty());
 7383        assert_eq!(
 7384            buffer.text(),
 7385            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7386        )
 7387    });
 7388    buffer_2.update(cx, |buffer, _| {
 7389        assert!(buffer.is_dirty());
 7390        assert_eq!(
 7391            buffer.text(),
 7392            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7393        )
 7394    });
 7395    buffer_3.update(cx, |buffer, _| {
 7396        assert!(!buffer.is_dirty());
 7397        assert_eq!(buffer.text(), sample_text_3,)
 7398    });
 7399    cx.executor().run_until_parked();
 7400
 7401    cx.executor().start_waiting();
 7402    let save = multi_buffer_editor
 7403        .update_in(cx, |editor, window, cx| {
 7404            editor.save(true, project.clone(), window, cx)
 7405        })
 7406        .unwrap();
 7407
 7408    let fake_server = fake_servers.next().await.unwrap();
 7409    fake_server
 7410        .server
 7411        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7412            Ok(Some(vec![lsp::TextEdit::new(
 7413                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7414                format!("[{} formatted]", params.text_document.uri),
 7415            )]))
 7416        })
 7417        .detach();
 7418    save.await;
 7419
 7420    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7421    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7422    assert_eq!(
 7423        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7424        "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}",
 7425    );
 7426    buffer_1.update(cx, |buffer, _| {
 7427        assert!(!buffer.is_dirty());
 7428        assert_eq!(
 7429            buffer.text(),
 7430            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7431        )
 7432    });
 7433    buffer_2.update(cx, |buffer, _| {
 7434        assert!(!buffer.is_dirty());
 7435        assert_eq!(
 7436            buffer.text(),
 7437            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7438        )
 7439    });
 7440    buffer_3.update(cx, |buffer, _| {
 7441        assert!(!buffer.is_dirty());
 7442        assert_eq!(buffer.text(), sample_text_3,)
 7443    });
 7444}
 7445
 7446#[gpui::test]
 7447async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7448    init_test(cx, |_| {});
 7449
 7450    let fs = FakeFs::new(cx.executor());
 7451    fs.insert_file("/file.rs", Default::default()).await;
 7452
 7453    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7454
 7455    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7456    language_registry.add(rust_lang());
 7457    let mut fake_servers = language_registry.register_fake_lsp(
 7458        "Rust",
 7459        FakeLspAdapter {
 7460            capabilities: lsp::ServerCapabilities {
 7461                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7462                ..Default::default()
 7463            },
 7464            ..Default::default()
 7465        },
 7466    );
 7467
 7468    let buffer = project
 7469        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7470        .await
 7471        .unwrap();
 7472
 7473    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7474    let (editor, cx) = cx.add_window_view(|window, cx| {
 7475        build_editor_with_project(project.clone(), buffer, window, cx)
 7476    });
 7477    editor.update_in(cx, |editor, window, cx| {
 7478        editor.set_text("one\ntwo\nthree\n", window, cx)
 7479    });
 7480    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7481
 7482    cx.executor().start_waiting();
 7483    let fake_server = fake_servers.next().await.unwrap();
 7484
 7485    let save = editor
 7486        .update_in(cx, |editor, window, cx| {
 7487            editor.save(true, project.clone(), window, cx)
 7488        })
 7489        .unwrap();
 7490    fake_server
 7491        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7492            assert_eq!(
 7493                params.text_document.uri,
 7494                lsp::Url::from_file_path("/file.rs").unwrap()
 7495            );
 7496            assert_eq!(params.options.tab_size, 4);
 7497            Ok(Some(vec![lsp::TextEdit::new(
 7498                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7499                ", ".to_string(),
 7500            )]))
 7501        })
 7502        .next()
 7503        .await;
 7504    cx.executor().start_waiting();
 7505    save.await;
 7506    assert_eq!(
 7507        editor.update(cx, |editor, cx| editor.text(cx)),
 7508        "one, two\nthree\n"
 7509    );
 7510    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7511
 7512    editor.update_in(cx, |editor, window, cx| {
 7513        editor.set_text("one\ntwo\nthree\n", window, cx)
 7514    });
 7515    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7516
 7517    // Ensure we can still save even if formatting hangs.
 7518    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7519        move |params, _| async move {
 7520            assert_eq!(
 7521                params.text_document.uri,
 7522                lsp::Url::from_file_path("/file.rs").unwrap()
 7523            );
 7524            futures::future::pending::<()>().await;
 7525            unreachable!()
 7526        },
 7527    );
 7528    let save = editor
 7529        .update_in(cx, |editor, window, cx| {
 7530            editor.save(true, project.clone(), window, cx)
 7531        })
 7532        .unwrap();
 7533    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7534    cx.executor().start_waiting();
 7535    save.await;
 7536    assert_eq!(
 7537        editor.update(cx, |editor, cx| editor.text(cx)),
 7538        "one\ntwo\nthree\n"
 7539    );
 7540    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7541
 7542    // For non-dirty buffer, no formatting request should be sent
 7543    let save = editor
 7544        .update_in(cx, |editor, window, cx| {
 7545            editor.save(true, project.clone(), window, cx)
 7546        })
 7547        .unwrap();
 7548    let _pending_format_request = fake_server
 7549        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7550            panic!("Should not be invoked on non-dirty buffer");
 7551        })
 7552        .next();
 7553    cx.executor().start_waiting();
 7554    save.await;
 7555
 7556    // Set Rust language override and assert overridden tabsize is sent to language server
 7557    update_test_language_settings(cx, |settings| {
 7558        settings.languages.insert(
 7559            "Rust".into(),
 7560            LanguageSettingsContent {
 7561                tab_size: NonZeroU32::new(8),
 7562                ..Default::default()
 7563            },
 7564        );
 7565    });
 7566
 7567    editor.update_in(cx, |editor, window, cx| {
 7568        editor.set_text("somehting_new\n", window, cx)
 7569    });
 7570    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7571    let save = editor
 7572        .update_in(cx, |editor, window, cx| {
 7573            editor.save(true, project.clone(), window, cx)
 7574        })
 7575        .unwrap();
 7576    fake_server
 7577        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7578            assert_eq!(
 7579                params.text_document.uri,
 7580                lsp::Url::from_file_path("/file.rs").unwrap()
 7581            );
 7582            assert_eq!(params.options.tab_size, 8);
 7583            Ok(Some(vec![]))
 7584        })
 7585        .next()
 7586        .await;
 7587    cx.executor().start_waiting();
 7588    save.await;
 7589}
 7590
 7591#[gpui::test]
 7592async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7593    init_test(cx, |settings| {
 7594        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7595            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7596        ))
 7597    });
 7598
 7599    let fs = FakeFs::new(cx.executor());
 7600    fs.insert_file("/file.rs", Default::default()).await;
 7601
 7602    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7603
 7604    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7605    language_registry.add(Arc::new(Language::new(
 7606        LanguageConfig {
 7607            name: "Rust".into(),
 7608            matcher: LanguageMatcher {
 7609                path_suffixes: vec!["rs".to_string()],
 7610                ..Default::default()
 7611            },
 7612            ..LanguageConfig::default()
 7613        },
 7614        Some(tree_sitter_rust::LANGUAGE.into()),
 7615    )));
 7616    update_test_language_settings(cx, |settings| {
 7617        // Enable Prettier formatting for the same buffer, and ensure
 7618        // LSP is called instead of Prettier.
 7619        settings.defaults.prettier = Some(PrettierSettings {
 7620            allowed: true,
 7621            ..PrettierSettings::default()
 7622        });
 7623    });
 7624    let mut fake_servers = language_registry.register_fake_lsp(
 7625        "Rust",
 7626        FakeLspAdapter {
 7627            capabilities: lsp::ServerCapabilities {
 7628                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7629                ..Default::default()
 7630            },
 7631            ..Default::default()
 7632        },
 7633    );
 7634
 7635    let buffer = project
 7636        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7637        .await
 7638        .unwrap();
 7639
 7640    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7641    let (editor, cx) = cx.add_window_view(|window, cx| {
 7642        build_editor_with_project(project.clone(), buffer, window, cx)
 7643    });
 7644    editor.update_in(cx, |editor, window, cx| {
 7645        editor.set_text("one\ntwo\nthree\n", window, cx)
 7646    });
 7647
 7648    cx.executor().start_waiting();
 7649    let fake_server = fake_servers.next().await.unwrap();
 7650
 7651    let format = editor
 7652        .update_in(cx, |editor, window, cx| {
 7653            editor.perform_format(
 7654                project.clone(),
 7655                FormatTrigger::Manual,
 7656                FormatTarget::Buffers,
 7657                window,
 7658                cx,
 7659            )
 7660        })
 7661        .unwrap();
 7662    fake_server
 7663        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7664            assert_eq!(
 7665                params.text_document.uri,
 7666                lsp::Url::from_file_path("/file.rs").unwrap()
 7667            );
 7668            assert_eq!(params.options.tab_size, 4);
 7669            Ok(Some(vec![lsp::TextEdit::new(
 7670                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7671                ", ".to_string(),
 7672            )]))
 7673        })
 7674        .next()
 7675        .await;
 7676    cx.executor().start_waiting();
 7677    format.await;
 7678    assert_eq!(
 7679        editor.update(cx, |editor, cx| editor.text(cx)),
 7680        "one, two\nthree\n"
 7681    );
 7682
 7683    editor.update_in(cx, |editor, window, cx| {
 7684        editor.set_text("one\ntwo\nthree\n", window, cx)
 7685    });
 7686    // Ensure we don't lock if formatting hangs.
 7687    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7688        assert_eq!(
 7689            params.text_document.uri,
 7690            lsp::Url::from_file_path("/file.rs").unwrap()
 7691        );
 7692        futures::future::pending::<()>().await;
 7693        unreachable!()
 7694    });
 7695    let format = editor
 7696        .update_in(cx, |editor, window, cx| {
 7697            editor.perform_format(
 7698                project,
 7699                FormatTrigger::Manual,
 7700                FormatTarget::Buffers,
 7701                window,
 7702                cx,
 7703            )
 7704        })
 7705        .unwrap();
 7706    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7707    cx.executor().start_waiting();
 7708    format.await;
 7709    assert_eq!(
 7710        editor.update(cx, |editor, cx| editor.text(cx)),
 7711        "one\ntwo\nthree\n"
 7712    );
 7713}
 7714
 7715#[gpui::test]
 7716async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7717    init_test(cx, |_| {});
 7718
 7719    let mut cx = EditorLspTestContext::new_rust(
 7720        lsp::ServerCapabilities {
 7721            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7722            ..Default::default()
 7723        },
 7724        cx,
 7725    )
 7726    .await;
 7727
 7728    cx.set_state(indoc! {"
 7729        one.twoˇ
 7730    "});
 7731
 7732    // The format request takes a long time. When it completes, it inserts
 7733    // a newline and an indent before the `.`
 7734    cx.lsp
 7735        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7736            let executor = cx.background_executor().clone();
 7737            async move {
 7738                executor.timer(Duration::from_millis(100)).await;
 7739                Ok(Some(vec![lsp::TextEdit {
 7740                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7741                    new_text: "\n    ".into(),
 7742                }]))
 7743            }
 7744        });
 7745
 7746    // Submit a format request.
 7747    let format_1 = cx
 7748        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7749        .unwrap();
 7750    cx.executor().run_until_parked();
 7751
 7752    // Submit a second format request.
 7753    let format_2 = cx
 7754        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7755        .unwrap();
 7756    cx.executor().run_until_parked();
 7757
 7758    // Wait for both format requests to complete
 7759    cx.executor().advance_clock(Duration::from_millis(200));
 7760    cx.executor().start_waiting();
 7761    format_1.await.unwrap();
 7762    cx.executor().start_waiting();
 7763    format_2.await.unwrap();
 7764
 7765    // The formatting edits only happens once.
 7766    cx.assert_editor_state(indoc! {"
 7767        one
 7768            .twoˇ
 7769    "});
 7770}
 7771
 7772#[gpui::test]
 7773async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7774    init_test(cx, |settings| {
 7775        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7776    });
 7777
 7778    let mut cx = EditorLspTestContext::new_rust(
 7779        lsp::ServerCapabilities {
 7780            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7781            ..Default::default()
 7782        },
 7783        cx,
 7784    )
 7785    .await;
 7786
 7787    // Set up a buffer white some trailing whitespace and no trailing newline.
 7788    cx.set_state(
 7789        &[
 7790            "one ",   //
 7791            "twoˇ",   //
 7792            "three ", //
 7793            "four",   //
 7794        ]
 7795        .join("\n"),
 7796    );
 7797
 7798    // Submit a format request.
 7799    let format = cx
 7800        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7801        .unwrap();
 7802
 7803    // Record which buffer changes have been sent to the language server
 7804    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7805    cx.lsp
 7806        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7807            let buffer_changes = buffer_changes.clone();
 7808            move |params, _| {
 7809                buffer_changes.lock().extend(
 7810                    params
 7811                        .content_changes
 7812                        .into_iter()
 7813                        .map(|e| (e.range.unwrap(), e.text)),
 7814                );
 7815            }
 7816        });
 7817
 7818    // Handle formatting requests to the language server.
 7819    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7820        let buffer_changes = buffer_changes.clone();
 7821        move |_, _| {
 7822            // When formatting is requested, trailing whitespace has already been stripped,
 7823            // and the trailing newline has already been added.
 7824            assert_eq!(
 7825                &buffer_changes.lock()[1..],
 7826                &[
 7827                    (
 7828                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7829                        "".into()
 7830                    ),
 7831                    (
 7832                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7833                        "".into()
 7834                    ),
 7835                    (
 7836                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7837                        "\n".into()
 7838                    ),
 7839                ]
 7840            );
 7841
 7842            // Insert blank lines between each line of the buffer.
 7843            async move {
 7844                Ok(Some(vec![
 7845                    lsp::TextEdit {
 7846                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7847                        new_text: "\n".into(),
 7848                    },
 7849                    lsp::TextEdit {
 7850                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7851                        new_text: "\n".into(),
 7852                    },
 7853                ]))
 7854            }
 7855        }
 7856    });
 7857
 7858    // After formatting the buffer, the trailing whitespace is stripped,
 7859    // a newline is appended, and the edits provided by the language server
 7860    // have been applied.
 7861    format.await.unwrap();
 7862    cx.assert_editor_state(
 7863        &[
 7864            "one",   //
 7865            "",      //
 7866            "twoˇ",  //
 7867            "",      //
 7868            "three", //
 7869            "four",  //
 7870            "",      //
 7871        ]
 7872        .join("\n"),
 7873    );
 7874
 7875    // Undoing the formatting undoes the trailing whitespace removal, the
 7876    // trailing newline, and the LSP edits.
 7877    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7878    cx.assert_editor_state(
 7879        &[
 7880            "one ",   //
 7881            "twoˇ",   //
 7882            "three ", //
 7883            "four",   //
 7884        ]
 7885        .join("\n"),
 7886    );
 7887}
 7888
 7889#[gpui::test]
 7890async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7891    cx: &mut gpui::TestAppContext,
 7892) {
 7893    init_test(cx, |_| {});
 7894
 7895    cx.update(|cx| {
 7896        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7897            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7898                settings.auto_signature_help = Some(true);
 7899            });
 7900        });
 7901    });
 7902
 7903    let mut cx = EditorLspTestContext::new_rust(
 7904        lsp::ServerCapabilities {
 7905            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7906                ..Default::default()
 7907            }),
 7908            ..Default::default()
 7909        },
 7910        cx,
 7911    )
 7912    .await;
 7913
 7914    let language = Language::new(
 7915        LanguageConfig {
 7916            name: "Rust".into(),
 7917            brackets: BracketPairConfig {
 7918                pairs: vec![
 7919                    BracketPair {
 7920                        start: "{".to_string(),
 7921                        end: "}".to_string(),
 7922                        close: true,
 7923                        surround: true,
 7924                        newline: true,
 7925                    },
 7926                    BracketPair {
 7927                        start: "(".to_string(),
 7928                        end: ")".to_string(),
 7929                        close: true,
 7930                        surround: true,
 7931                        newline: true,
 7932                    },
 7933                    BracketPair {
 7934                        start: "/*".to_string(),
 7935                        end: " */".to_string(),
 7936                        close: true,
 7937                        surround: true,
 7938                        newline: true,
 7939                    },
 7940                    BracketPair {
 7941                        start: "[".to_string(),
 7942                        end: "]".to_string(),
 7943                        close: false,
 7944                        surround: false,
 7945                        newline: true,
 7946                    },
 7947                    BracketPair {
 7948                        start: "\"".to_string(),
 7949                        end: "\"".to_string(),
 7950                        close: true,
 7951                        surround: true,
 7952                        newline: false,
 7953                    },
 7954                    BracketPair {
 7955                        start: "<".to_string(),
 7956                        end: ">".to_string(),
 7957                        close: false,
 7958                        surround: true,
 7959                        newline: true,
 7960                    },
 7961                ],
 7962                ..Default::default()
 7963            },
 7964            autoclose_before: "})]".to_string(),
 7965            ..Default::default()
 7966        },
 7967        Some(tree_sitter_rust::LANGUAGE.into()),
 7968    );
 7969    let language = Arc::new(language);
 7970
 7971    cx.language_registry().add(language.clone());
 7972    cx.update_buffer(|buffer, cx| {
 7973        buffer.set_language(Some(language), cx);
 7974    });
 7975
 7976    cx.set_state(
 7977        &r#"
 7978            fn main() {
 7979                sampleˇ
 7980            }
 7981        "#
 7982        .unindent(),
 7983    );
 7984
 7985    cx.update_editor(|editor, window, cx| {
 7986        editor.handle_input("(", window, cx);
 7987    });
 7988    cx.assert_editor_state(
 7989        &"
 7990            fn main() {
 7991                sample(ˇ)
 7992            }
 7993        "
 7994        .unindent(),
 7995    );
 7996
 7997    let mocked_response = lsp::SignatureHelp {
 7998        signatures: vec![lsp::SignatureInformation {
 7999            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8000            documentation: None,
 8001            parameters: Some(vec![
 8002                lsp::ParameterInformation {
 8003                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8004                    documentation: None,
 8005                },
 8006                lsp::ParameterInformation {
 8007                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8008                    documentation: None,
 8009                },
 8010            ]),
 8011            active_parameter: None,
 8012        }],
 8013        active_signature: Some(0),
 8014        active_parameter: Some(0),
 8015    };
 8016    handle_signature_help_request(&mut cx, mocked_response).await;
 8017
 8018    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8019        .await;
 8020
 8021    cx.editor(|editor, _, _| {
 8022        let signature_help_state = editor.signature_help_state.popover().cloned();
 8023        assert!(signature_help_state.is_some());
 8024        let ParsedMarkdown {
 8025            text, highlights, ..
 8026        } = signature_help_state.unwrap().parsed_content;
 8027        assert_eq!(text, "param1: u8, param2: u8");
 8028        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8029    });
 8030}
 8031
 8032#[gpui::test]
 8033async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8034    init_test(cx, |_| {});
 8035
 8036    cx.update(|cx| {
 8037        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8038            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8039                settings.auto_signature_help = Some(false);
 8040                settings.show_signature_help_after_edits = Some(false);
 8041            });
 8042        });
 8043    });
 8044
 8045    let mut cx = EditorLspTestContext::new_rust(
 8046        lsp::ServerCapabilities {
 8047            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8048                ..Default::default()
 8049            }),
 8050            ..Default::default()
 8051        },
 8052        cx,
 8053    )
 8054    .await;
 8055
 8056    let language = Language::new(
 8057        LanguageConfig {
 8058            name: "Rust".into(),
 8059            brackets: BracketPairConfig {
 8060                pairs: vec![
 8061                    BracketPair {
 8062                        start: "{".to_string(),
 8063                        end: "}".to_string(),
 8064                        close: true,
 8065                        surround: true,
 8066                        newline: true,
 8067                    },
 8068                    BracketPair {
 8069                        start: "(".to_string(),
 8070                        end: ")".to_string(),
 8071                        close: true,
 8072                        surround: true,
 8073                        newline: true,
 8074                    },
 8075                    BracketPair {
 8076                        start: "/*".to_string(),
 8077                        end: " */".to_string(),
 8078                        close: true,
 8079                        surround: true,
 8080                        newline: true,
 8081                    },
 8082                    BracketPair {
 8083                        start: "[".to_string(),
 8084                        end: "]".to_string(),
 8085                        close: false,
 8086                        surround: false,
 8087                        newline: true,
 8088                    },
 8089                    BracketPair {
 8090                        start: "\"".to_string(),
 8091                        end: "\"".to_string(),
 8092                        close: true,
 8093                        surround: true,
 8094                        newline: false,
 8095                    },
 8096                    BracketPair {
 8097                        start: "<".to_string(),
 8098                        end: ">".to_string(),
 8099                        close: false,
 8100                        surround: true,
 8101                        newline: true,
 8102                    },
 8103                ],
 8104                ..Default::default()
 8105            },
 8106            autoclose_before: "})]".to_string(),
 8107            ..Default::default()
 8108        },
 8109        Some(tree_sitter_rust::LANGUAGE.into()),
 8110    );
 8111    let language = Arc::new(language);
 8112
 8113    cx.language_registry().add(language.clone());
 8114    cx.update_buffer(|buffer, cx| {
 8115        buffer.set_language(Some(language), cx);
 8116    });
 8117
 8118    // Ensure that signature_help is not called when no signature help is enabled.
 8119    cx.set_state(
 8120        &r#"
 8121            fn main() {
 8122                sampleˇ
 8123            }
 8124        "#
 8125        .unindent(),
 8126    );
 8127    cx.update_editor(|editor, window, cx| {
 8128        editor.handle_input("(", window, cx);
 8129    });
 8130    cx.assert_editor_state(
 8131        &"
 8132            fn main() {
 8133                sample(ˇ)
 8134            }
 8135        "
 8136        .unindent(),
 8137    );
 8138    cx.editor(|editor, _, _| {
 8139        assert!(editor.signature_help_state.task().is_none());
 8140    });
 8141
 8142    let mocked_response = lsp::SignatureHelp {
 8143        signatures: vec![lsp::SignatureInformation {
 8144            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8145            documentation: None,
 8146            parameters: Some(vec![
 8147                lsp::ParameterInformation {
 8148                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8149                    documentation: None,
 8150                },
 8151                lsp::ParameterInformation {
 8152                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8153                    documentation: None,
 8154                },
 8155            ]),
 8156            active_parameter: None,
 8157        }],
 8158        active_signature: Some(0),
 8159        active_parameter: Some(0),
 8160    };
 8161
 8162    // Ensure that signature_help is called when enabled afte edits
 8163    cx.update(|_, cx| {
 8164        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8165            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8166                settings.auto_signature_help = Some(false);
 8167                settings.show_signature_help_after_edits = Some(true);
 8168            });
 8169        });
 8170    });
 8171    cx.set_state(
 8172        &r#"
 8173            fn main() {
 8174                sampleˇ
 8175            }
 8176        "#
 8177        .unindent(),
 8178    );
 8179    cx.update_editor(|editor, window, cx| {
 8180        editor.handle_input("(", window, cx);
 8181    });
 8182    cx.assert_editor_state(
 8183        &"
 8184            fn main() {
 8185                sample(ˇ)
 8186            }
 8187        "
 8188        .unindent(),
 8189    );
 8190    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8191    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8192        .await;
 8193    cx.update_editor(|editor, _, _| {
 8194        let signature_help_state = editor.signature_help_state.popover().cloned();
 8195        assert!(signature_help_state.is_some());
 8196        let ParsedMarkdown {
 8197            text, highlights, ..
 8198        } = signature_help_state.unwrap().parsed_content;
 8199        assert_eq!(text, "param1: u8, param2: u8");
 8200        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8201        editor.signature_help_state = SignatureHelpState::default();
 8202    });
 8203
 8204    // Ensure that signature_help is called when auto signature help override is enabled
 8205    cx.update(|_, cx| {
 8206        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8207            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8208                settings.auto_signature_help = Some(true);
 8209                settings.show_signature_help_after_edits = Some(false);
 8210            });
 8211        });
 8212    });
 8213    cx.set_state(
 8214        &r#"
 8215            fn main() {
 8216                sampleˇ
 8217            }
 8218        "#
 8219        .unindent(),
 8220    );
 8221    cx.update_editor(|editor, window, cx| {
 8222        editor.handle_input("(", window, cx);
 8223    });
 8224    cx.assert_editor_state(
 8225        &"
 8226            fn main() {
 8227                sample(ˇ)
 8228            }
 8229        "
 8230        .unindent(),
 8231    );
 8232    handle_signature_help_request(&mut cx, mocked_response).await;
 8233    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8234        .await;
 8235    cx.editor(|editor, _, _| {
 8236        let signature_help_state = editor.signature_help_state.popover().cloned();
 8237        assert!(signature_help_state.is_some());
 8238        let ParsedMarkdown {
 8239            text, highlights, ..
 8240        } = signature_help_state.unwrap().parsed_content;
 8241        assert_eq!(text, "param1: u8, param2: u8");
 8242        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8243    });
 8244}
 8245
 8246#[gpui::test]
 8247async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8248    init_test(cx, |_| {});
 8249    cx.update(|cx| {
 8250        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8251            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8252                settings.auto_signature_help = Some(true);
 8253            });
 8254        });
 8255    });
 8256
 8257    let mut cx = EditorLspTestContext::new_rust(
 8258        lsp::ServerCapabilities {
 8259            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8260                ..Default::default()
 8261            }),
 8262            ..Default::default()
 8263        },
 8264        cx,
 8265    )
 8266    .await;
 8267
 8268    // A test that directly calls `show_signature_help`
 8269    cx.update_editor(|editor, window, cx| {
 8270        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8271    });
 8272
 8273    let mocked_response = lsp::SignatureHelp {
 8274        signatures: vec![lsp::SignatureInformation {
 8275            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8276            documentation: None,
 8277            parameters: Some(vec![
 8278                lsp::ParameterInformation {
 8279                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8280                    documentation: None,
 8281                },
 8282                lsp::ParameterInformation {
 8283                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8284                    documentation: None,
 8285                },
 8286            ]),
 8287            active_parameter: None,
 8288        }],
 8289        active_signature: Some(0),
 8290        active_parameter: Some(0),
 8291    };
 8292    handle_signature_help_request(&mut cx, mocked_response).await;
 8293
 8294    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8295        .await;
 8296
 8297    cx.editor(|editor, _, _| {
 8298        let signature_help_state = editor.signature_help_state.popover().cloned();
 8299        assert!(signature_help_state.is_some());
 8300        let ParsedMarkdown {
 8301            text, highlights, ..
 8302        } = signature_help_state.unwrap().parsed_content;
 8303        assert_eq!(text, "param1: u8, param2: u8");
 8304        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8305    });
 8306
 8307    // When exiting outside from inside the brackets, `signature_help` is closed.
 8308    cx.set_state(indoc! {"
 8309        fn main() {
 8310            sample(ˇ);
 8311        }
 8312
 8313        fn sample(param1: u8, param2: u8) {}
 8314    "});
 8315
 8316    cx.update_editor(|editor, window, cx| {
 8317        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8318    });
 8319
 8320    let mocked_response = lsp::SignatureHelp {
 8321        signatures: Vec::new(),
 8322        active_signature: None,
 8323        active_parameter: None,
 8324    };
 8325    handle_signature_help_request(&mut cx, mocked_response).await;
 8326
 8327    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8328        .await;
 8329
 8330    cx.editor(|editor, _, _| {
 8331        assert!(!editor.signature_help_state.is_shown());
 8332    });
 8333
 8334    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8335    cx.set_state(indoc! {"
 8336        fn main() {
 8337            sample(ˇ);
 8338        }
 8339
 8340        fn sample(param1: u8, param2: u8) {}
 8341    "});
 8342
 8343    let mocked_response = lsp::SignatureHelp {
 8344        signatures: vec![lsp::SignatureInformation {
 8345            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8346            documentation: None,
 8347            parameters: Some(vec![
 8348                lsp::ParameterInformation {
 8349                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8350                    documentation: None,
 8351                },
 8352                lsp::ParameterInformation {
 8353                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8354                    documentation: None,
 8355                },
 8356            ]),
 8357            active_parameter: None,
 8358        }],
 8359        active_signature: Some(0),
 8360        active_parameter: Some(0),
 8361    };
 8362    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8363    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8364        .await;
 8365    cx.editor(|editor, _, _| {
 8366        assert!(editor.signature_help_state.is_shown());
 8367    });
 8368
 8369    // Restore the popover with more parameter input
 8370    cx.set_state(indoc! {"
 8371        fn main() {
 8372            sample(param1, param2ˇ);
 8373        }
 8374
 8375        fn sample(param1: u8, param2: u8) {}
 8376    "});
 8377
 8378    let mocked_response = lsp::SignatureHelp {
 8379        signatures: vec![lsp::SignatureInformation {
 8380            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8381            documentation: None,
 8382            parameters: Some(vec![
 8383                lsp::ParameterInformation {
 8384                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8385                    documentation: None,
 8386                },
 8387                lsp::ParameterInformation {
 8388                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8389                    documentation: None,
 8390                },
 8391            ]),
 8392            active_parameter: None,
 8393        }],
 8394        active_signature: Some(0),
 8395        active_parameter: Some(1),
 8396    };
 8397    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8398    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8399        .await;
 8400
 8401    // When selecting a range, the popover is gone.
 8402    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8403    cx.update_editor(|editor, window, cx| {
 8404        editor.change_selections(None, window, cx, |s| {
 8405            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8406        })
 8407    });
 8408    cx.assert_editor_state(indoc! {"
 8409        fn main() {
 8410            sample(param1, «ˇparam2»);
 8411        }
 8412
 8413        fn sample(param1: u8, param2: u8) {}
 8414    "});
 8415    cx.editor(|editor, _, _| {
 8416        assert!(!editor.signature_help_state.is_shown());
 8417    });
 8418
 8419    // When unselecting again, the popover is back if within the brackets.
 8420    cx.update_editor(|editor, window, cx| {
 8421        editor.change_selections(None, window, cx, |s| {
 8422            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8423        })
 8424    });
 8425    cx.assert_editor_state(indoc! {"
 8426        fn main() {
 8427            sample(param1, ˇparam2);
 8428        }
 8429
 8430        fn sample(param1: u8, param2: u8) {}
 8431    "});
 8432    handle_signature_help_request(&mut cx, mocked_response).await;
 8433    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8434        .await;
 8435    cx.editor(|editor, _, _| {
 8436        assert!(editor.signature_help_state.is_shown());
 8437    });
 8438
 8439    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8440    cx.update_editor(|editor, window, cx| {
 8441        editor.change_selections(None, window, cx, |s| {
 8442            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8443            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8444        })
 8445    });
 8446    cx.assert_editor_state(indoc! {"
 8447        fn main() {
 8448            sample(param1, ˇparam2);
 8449        }
 8450
 8451        fn sample(param1: u8, param2: u8) {}
 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(1),
 8472    };
 8473    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8474    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8475        .await;
 8476    cx.update_editor(|editor, _, cx| {
 8477        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8478    });
 8479    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8480        .await;
 8481    cx.update_editor(|editor, window, cx| {
 8482        editor.change_selections(None, window, cx, |s| {
 8483            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8484        })
 8485    });
 8486    cx.assert_editor_state(indoc! {"
 8487        fn main() {
 8488            sample(param1, «ˇparam2»);
 8489        }
 8490
 8491        fn sample(param1: u8, param2: u8) {}
 8492    "});
 8493    cx.update_editor(|editor, window, cx| {
 8494        editor.change_selections(None, window, cx, |s| {
 8495            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8496        })
 8497    });
 8498    cx.assert_editor_state(indoc! {"
 8499        fn main() {
 8500            sample(param1, ˇparam2);
 8501        }
 8502
 8503        fn sample(param1: u8, param2: u8) {}
 8504    "});
 8505    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8506        .await;
 8507}
 8508
 8509#[gpui::test]
 8510async fn test_completion(cx: &mut gpui::TestAppContext) {
 8511    init_test(cx, |_| {});
 8512
 8513    let mut cx = EditorLspTestContext::new_rust(
 8514        lsp::ServerCapabilities {
 8515            completion_provider: Some(lsp::CompletionOptions {
 8516                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8517                resolve_provider: Some(true),
 8518                ..Default::default()
 8519            }),
 8520            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8521            ..Default::default()
 8522        },
 8523        cx,
 8524    )
 8525    .await;
 8526    let counter = Arc::new(AtomicUsize::new(0));
 8527
 8528    cx.set_state(indoc! {"
 8529        oneˇ
 8530        two
 8531        three
 8532    "});
 8533    cx.simulate_keystroke(".");
 8534    handle_completion_request(
 8535        &mut cx,
 8536        indoc! {"
 8537            one.|<>
 8538            two
 8539            three
 8540        "},
 8541        vec!["first_completion", "second_completion"],
 8542        counter.clone(),
 8543    )
 8544    .await;
 8545    cx.condition(|editor, _| editor.context_menu_visible())
 8546        .await;
 8547    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8548
 8549    let _handler = handle_signature_help_request(
 8550        &mut cx,
 8551        lsp::SignatureHelp {
 8552            signatures: vec![lsp::SignatureInformation {
 8553                label: "test signature".to_string(),
 8554                documentation: None,
 8555                parameters: Some(vec![lsp::ParameterInformation {
 8556                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8557                    documentation: None,
 8558                }]),
 8559                active_parameter: None,
 8560            }],
 8561            active_signature: None,
 8562            active_parameter: None,
 8563        },
 8564    );
 8565    cx.update_editor(|editor, window, cx| {
 8566        assert!(
 8567            !editor.signature_help_state.is_shown(),
 8568            "No signature help was called for"
 8569        );
 8570        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8571    });
 8572    cx.run_until_parked();
 8573    cx.update_editor(|editor, _, _| {
 8574        assert!(
 8575            !editor.signature_help_state.is_shown(),
 8576            "No signature help should be shown when completions menu is open"
 8577        );
 8578    });
 8579
 8580    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8581        editor.context_menu_next(&Default::default(), window, cx);
 8582        editor
 8583            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8584            .unwrap()
 8585    });
 8586    cx.assert_editor_state(indoc! {"
 8587        one.second_completionˇ
 8588        two
 8589        three
 8590    "});
 8591
 8592    handle_resolve_completion_request(
 8593        &mut cx,
 8594        Some(vec![
 8595            (
 8596                //This overlaps with the primary completion edit which is
 8597                //misbehavior from the LSP spec, test that we filter it out
 8598                indoc! {"
 8599                    one.second_ˇcompletion
 8600                    two
 8601                    threeˇ
 8602                "},
 8603                "overlapping additional edit",
 8604            ),
 8605            (
 8606                indoc! {"
 8607                    one.second_completion
 8608                    two
 8609                    threeˇ
 8610                "},
 8611                "\nadditional edit",
 8612            ),
 8613        ]),
 8614    )
 8615    .await;
 8616    apply_additional_edits.await.unwrap();
 8617    cx.assert_editor_state(indoc! {"
 8618        one.second_completionˇ
 8619        two
 8620        three
 8621        additional edit
 8622    "});
 8623
 8624    cx.set_state(indoc! {"
 8625        one.second_completion
 8626        twoˇ
 8627        threeˇ
 8628        additional edit
 8629    "});
 8630    cx.simulate_keystroke(" ");
 8631    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8632    cx.simulate_keystroke("s");
 8633    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8634
 8635    cx.assert_editor_state(indoc! {"
 8636        one.second_completion
 8637        two sˇ
 8638        three sˇ
 8639        additional edit
 8640    "});
 8641    handle_completion_request(
 8642        &mut cx,
 8643        indoc! {"
 8644            one.second_completion
 8645            two s
 8646            three <s|>
 8647            additional edit
 8648        "},
 8649        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8650        counter.clone(),
 8651    )
 8652    .await;
 8653    cx.condition(|editor, _| editor.context_menu_visible())
 8654        .await;
 8655    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8656
 8657    cx.simulate_keystroke("i");
 8658
 8659    handle_completion_request(
 8660        &mut cx,
 8661        indoc! {"
 8662            one.second_completion
 8663            two si
 8664            three <si|>
 8665            additional edit
 8666        "},
 8667        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8668        counter.clone(),
 8669    )
 8670    .await;
 8671    cx.condition(|editor, _| editor.context_menu_visible())
 8672        .await;
 8673    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8674
 8675    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8676        editor
 8677            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8678            .unwrap()
 8679    });
 8680    cx.assert_editor_state(indoc! {"
 8681        one.second_completion
 8682        two sixth_completionˇ
 8683        three sixth_completionˇ
 8684        additional edit
 8685    "});
 8686
 8687    apply_additional_edits.await.unwrap();
 8688
 8689    update_test_language_settings(&mut cx, |settings| {
 8690        settings.defaults.show_completions_on_input = Some(false);
 8691    });
 8692    cx.set_state("editorˇ");
 8693    cx.simulate_keystroke(".");
 8694    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8695    cx.simulate_keystroke("c");
 8696    cx.simulate_keystroke("l");
 8697    cx.simulate_keystroke("o");
 8698    cx.assert_editor_state("editor.cloˇ");
 8699    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8700    cx.update_editor(|editor, window, cx| {
 8701        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8702    });
 8703    handle_completion_request(
 8704        &mut cx,
 8705        "editor.<clo|>",
 8706        vec!["close", "clobber"],
 8707        counter.clone(),
 8708    )
 8709    .await;
 8710    cx.condition(|editor, _| editor.context_menu_visible())
 8711        .await;
 8712    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8713
 8714    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8715        editor
 8716            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8717            .unwrap()
 8718    });
 8719    cx.assert_editor_state("editor.closeˇ");
 8720    handle_resolve_completion_request(&mut cx, None).await;
 8721    apply_additional_edits.await.unwrap();
 8722}
 8723
 8724#[gpui::test]
 8725async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8726    init_test(cx, |_| {});
 8727
 8728    let fs = FakeFs::new(cx.executor());
 8729    fs.insert_tree(
 8730        "/a",
 8731        json!({
 8732            "main.ts": "a",
 8733        }),
 8734    )
 8735    .await;
 8736
 8737    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8738    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8739    let typescript_language = Arc::new(Language::new(
 8740        LanguageConfig {
 8741            name: "TypeScript".into(),
 8742            matcher: LanguageMatcher {
 8743                path_suffixes: vec!["ts".to_string()],
 8744                ..LanguageMatcher::default()
 8745            },
 8746            line_comments: vec!["// ".into()],
 8747            ..LanguageConfig::default()
 8748        },
 8749        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8750    ));
 8751    language_registry.add(typescript_language.clone());
 8752    let mut fake_servers = language_registry.register_fake_lsp(
 8753        "TypeScript",
 8754        FakeLspAdapter {
 8755            capabilities: lsp::ServerCapabilities {
 8756                completion_provider: Some(lsp::CompletionOptions {
 8757                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8758                    ..lsp::CompletionOptions::default()
 8759                }),
 8760                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8761                ..lsp::ServerCapabilities::default()
 8762            },
 8763            // Emulate vtsls label generation
 8764            label_for_completion: Some(Box::new(|item, _| {
 8765                let text = if let Some(description) = item
 8766                    .label_details
 8767                    .as_ref()
 8768                    .and_then(|label_details| label_details.description.as_ref())
 8769                {
 8770                    format!("{} {}", item.label, description)
 8771                } else if let Some(detail) = &item.detail {
 8772                    format!("{} {}", item.label, detail)
 8773                } else {
 8774                    item.label.clone()
 8775                };
 8776                let len = text.len();
 8777                Some(language::CodeLabel {
 8778                    text,
 8779                    runs: Vec::new(),
 8780                    filter_range: 0..len,
 8781                })
 8782            })),
 8783            ..FakeLspAdapter::default()
 8784        },
 8785    );
 8786    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8787    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8788    let worktree_id = workspace
 8789        .update(cx, |workspace, _window, cx| {
 8790            workspace.project().update(cx, |project, cx| {
 8791                project.worktrees(cx).next().unwrap().read(cx).id()
 8792            })
 8793        })
 8794        .unwrap();
 8795    let _buffer = project
 8796        .update(cx, |project, cx| {
 8797            project.open_local_buffer_with_lsp("/a/main.ts", cx)
 8798        })
 8799        .await
 8800        .unwrap();
 8801    let editor = workspace
 8802        .update(cx, |workspace, window, cx| {
 8803            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8804        })
 8805        .unwrap()
 8806        .await
 8807        .unwrap()
 8808        .downcast::<Editor>()
 8809        .unwrap();
 8810    let fake_server = fake_servers.next().await.unwrap();
 8811
 8812    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8813    let multiline_label_2 = "a\nb\nc\n";
 8814    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8815    let multiline_description = "d\ne\nf\n";
 8816    let multiline_detail_2 = "g\nh\ni\n";
 8817
 8818    let mut completion_handle =
 8819        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8820            Ok(Some(lsp::CompletionResponse::Array(vec![
 8821                lsp::CompletionItem {
 8822                    label: multiline_label.to_string(),
 8823                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8824                        range: lsp::Range {
 8825                            start: lsp::Position {
 8826                                line: params.text_document_position.position.line,
 8827                                character: params.text_document_position.position.character,
 8828                            },
 8829                            end: lsp::Position {
 8830                                line: params.text_document_position.position.line,
 8831                                character: params.text_document_position.position.character,
 8832                            },
 8833                        },
 8834                        new_text: "new_text_1".to_string(),
 8835                    })),
 8836                    ..lsp::CompletionItem::default()
 8837                },
 8838                lsp::CompletionItem {
 8839                    label: "single line label 1".to_string(),
 8840                    detail: Some(multiline_detail.to_string()),
 8841                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8842                        range: lsp::Range {
 8843                            start: lsp::Position {
 8844                                line: params.text_document_position.position.line,
 8845                                character: params.text_document_position.position.character,
 8846                            },
 8847                            end: lsp::Position {
 8848                                line: params.text_document_position.position.line,
 8849                                character: params.text_document_position.position.character,
 8850                            },
 8851                        },
 8852                        new_text: "new_text_2".to_string(),
 8853                    })),
 8854                    ..lsp::CompletionItem::default()
 8855                },
 8856                lsp::CompletionItem {
 8857                    label: "single line label 2".to_string(),
 8858                    label_details: Some(lsp::CompletionItemLabelDetails {
 8859                        description: Some(multiline_description.to_string()),
 8860                        detail: None,
 8861                    }),
 8862                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8863                        range: lsp::Range {
 8864                            start: lsp::Position {
 8865                                line: params.text_document_position.position.line,
 8866                                character: params.text_document_position.position.character,
 8867                            },
 8868                            end: lsp::Position {
 8869                                line: params.text_document_position.position.line,
 8870                                character: params.text_document_position.position.character,
 8871                            },
 8872                        },
 8873                        new_text: "new_text_2".to_string(),
 8874                    })),
 8875                    ..lsp::CompletionItem::default()
 8876                },
 8877                lsp::CompletionItem {
 8878                    label: multiline_label_2.to_string(),
 8879                    detail: Some(multiline_detail_2.to_string()),
 8880                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8881                        range: lsp::Range {
 8882                            start: lsp::Position {
 8883                                line: params.text_document_position.position.line,
 8884                                character: params.text_document_position.position.character,
 8885                            },
 8886                            end: lsp::Position {
 8887                                line: params.text_document_position.position.line,
 8888                                character: params.text_document_position.position.character,
 8889                            },
 8890                        },
 8891                        new_text: "new_text_3".to_string(),
 8892                    })),
 8893                    ..lsp::CompletionItem::default()
 8894                },
 8895                lsp::CompletionItem {
 8896                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8897                    detail: Some(
 8898                        "Details with many     spaces and \t but without newlines".to_string(),
 8899                    ),
 8900                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8901                        range: lsp::Range {
 8902                            start: lsp::Position {
 8903                                line: params.text_document_position.position.line,
 8904                                character: params.text_document_position.position.character,
 8905                            },
 8906                            end: lsp::Position {
 8907                                line: params.text_document_position.position.line,
 8908                                character: params.text_document_position.position.character,
 8909                            },
 8910                        },
 8911                        new_text: "new_text_4".to_string(),
 8912                    })),
 8913                    ..lsp::CompletionItem::default()
 8914                },
 8915            ])))
 8916        });
 8917
 8918    editor.update_in(cx, |editor, window, cx| {
 8919        cx.focus_self(window);
 8920        editor.move_to_end(&MoveToEnd, window, cx);
 8921        editor.handle_input(".", window, cx);
 8922    });
 8923    cx.run_until_parked();
 8924    completion_handle.next().await.unwrap();
 8925
 8926    editor.update(cx, |editor, _| {
 8927        assert!(editor.context_menu_visible());
 8928        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8929        {
 8930            let completion_labels = menu
 8931                .completions
 8932                .borrow()
 8933                .iter()
 8934                .map(|c| c.label.text.clone())
 8935                .collect::<Vec<_>>();
 8936            assert_eq!(
 8937                completion_labels,
 8938                &[
 8939                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8940                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8941                    "single line label 2 d e f ",
 8942                    "a b c g h i ",
 8943                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8944                ],
 8945                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8946            );
 8947
 8948            for completion in menu
 8949                .completions
 8950                .borrow()
 8951                .iter() {
 8952                    assert_eq!(
 8953                        completion.label.filter_range,
 8954                        0..completion.label.text.len(),
 8955                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8956                    );
 8957                }
 8958
 8959        } else {
 8960            panic!("expected completion menu to be open");
 8961        }
 8962    });
 8963}
 8964
 8965#[gpui::test]
 8966async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8967    init_test(cx, |_| {});
 8968    let mut cx = EditorLspTestContext::new_rust(
 8969        lsp::ServerCapabilities {
 8970            completion_provider: Some(lsp::CompletionOptions {
 8971                trigger_characters: Some(vec![".".to_string()]),
 8972                ..Default::default()
 8973            }),
 8974            ..Default::default()
 8975        },
 8976        cx,
 8977    )
 8978    .await;
 8979    cx.lsp
 8980        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8981            Ok(Some(lsp::CompletionResponse::Array(vec![
 8982                lsp::CompletionItem {
 8983                    label: "first".into(),
 8984                    ..Default::default()
 8985                },
 8986                lsp::CompletionItem {
 8987                    label: "last".into(),
 8988                    ..Default::default()
 8989                },
 8990            ])))
 8991        });
 8992    cx.set_state("variableˇ");
 8993    cx.simulate_keystroke(".");
 8994    cx.executor().run_until_parked();
 8995
 8996    cx.update_editor(|editor, _, _| {
 8997        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8998        {
 8999            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9000        } else {
 9001            panic!("expected completion menu to be open");
 9002        }
 9003    });
 9004
 9005    cx.update_editor(|editor, window, cx| {
 9006        editor.move_page_down(&MovePageDown::default(), window, cx);
 9007        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9008        {
 9009            assert!(
 9010                menu.selected_item == 1,
 9011                "expected PageDown to select the last item from the context menu"
 9012            );
 9013        } else {
 9014            panic!("expected completion menu to stay open after PageDown");
 9015        }
 9016    });
 9017
 9018    cx.update_editor(|editor, window, cx| {
 9019        editor.move_page_up(&MovePageUp::default(), window, cx);
 9020        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9021        {
 9022            assert!(
 9023                menu.selected_item == 0,
 9024                "expected PageUp to select the first item from the context menu"
 9025            );
 9026        } else {
 9027            panic!("expected completion menu to stay open after PageUp");
 9028        }
 9029    });
 9030}
 9031
 9032#[gpui::test]
 9033async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9034    init_test(cx, |_| {});
 9035    let mut cx = EditorLspTestContext::new_rust(
 9036        lsp::ServerCapabilities {
 9037            completion_provider: Some(lsp::CompletionOptions {
 9038                trigger_characters: Some(vec![".".to_string()]),
 9039                ..Default::default()
 9040            }),
 9041            ..Default::default()
 9042        },
 9043        cx,
 9044    )
 9045    .await;
 9046    cx.lsp
 9047        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9048            Ok(Some(lsp::CompletionResponse::Array(vec![
 9049                lsp::CompletionItem {
 9050                    label: "Range".into(),
 9051                    sort_text: Some("a".into()),
 9052                    ..Default::default()
 9053                },
 9054                lsp::CompletionItem {
 9055                    label: "r".into(),
 9056                    sort_text: Some("b".into()),
 9057                    ..Default::default()
 9058                },
 9059                lsp::CompletionItem {
 9060                    label: "ret".into(),
 9061                    sort_text: Some("c".into()),
 9062                    ..Default::default()
 9063                },
 9064                lsp::CompletionItem {
 9065                    label: "return".into(),
 9066                    sort_text: Some("d".into()),
 9067                    ..Default::default()
 9068                },
 9069                lsp::CompletionItem {
 9070                    label: "slice".into(),
 9071                    sort_text: Some("d".into()),
 9072                    ..Default::default()
 9073                },
 9074            ])))
 9075        });
 9076    cx.set_state("");
 9077    cx.executor().run_until_parked();
 9078    cx.update_editor(|editor, window, cx| {
 9079        editor.show_completions(
 9080            &ShowCompletions {
 9081                trigger: Some("r".into()),
 9082            },
 9083            window,
 9084            cx,
 9085        );
 9086    });
 9087    cx.executor().run_until_parked();
 9088
 9089    cx.update_editor(|editor, _, _| {
 9090        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9091        {
 9092            assert_eq!(
 9093                completion_menu_entries(&menu),
 9094                &["r", "ret", "Range", "return"]
 9095            );
 9096        } else {
 9097            panic!("expected completion menu to be open");
 9098        }
 9099    });
 9100}
 9101
 9102#[gpui::test]
 9103async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9104    init_test(cx, |_| {});
 9105
 9106    let mut cx = EditorLspTestContext::new_rust(
 9107        lsp::ServerCapabilities {
 9108            completion_provider: Some(lsp::CompletionOptions {
 9109                trigger_characters: Some(vec![".".to_string()]),
 9110                resolve_provider: Some(true),
 9111                ..Default::default()
 9112            }),
 9113            ..Default::default()
 9114        },
 9115        cx,
 9116    )
 9117    .await;
 9118
 9119    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9120    cx.simulate_keystroke(".");
 9121    let completion_item = lsp::CompletionItem {
 9122        label: "Some".into(),
 9123        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9124        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9125        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9126            kind: lsp::MarkupKind::Markdown,
 9127            value: "```rust\nSome(2)\n```".to_string(),
 9128        })),
 9129        deprecated: Some(false),
 9130        sort_text: Some("Some".to_string()),
 9131        filter_text: Some("Some".to_string()),
 9132        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9133        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9134            range: lsp::Range {
 9135                start: lsp::Position {
 9136                    line: 0,
 9137                    character: 22,
 9138                },
 9139                end: lsp::Position {
 9140                    line: 0,
 9141                    character: 22,
 9142                },
 9143            },
 9144            new_text: "Some(2)".to_string(),
 9145        })),
 9146        additional_text_edits: Some(vec![lsp::TextEdit {
 9147            range: lsp::Range {
 9148                start: lsp::Position {
 9149                    line: 0,
 9150                    character: 20,
 9151                },
 9152                end: lsp::Position {
 9153                    line: 0,
 9154                    character: 22,
 9155                },
 9156            },
 9157            new_text: "".to_string(),
 9158        }]),
 9159        ..Default::default()
 9160    };
 9161
 9162    let closure_completion_item = completion_item.clone();
 9163    let counter = Arc::new(AtomicUsize::new(0));
 9164    let counter_clone = counter.clone();
 9165    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9166        let task_completion_item = closure_completion_item.clone();
 9167        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9168        async move {
 9169            Ok(Some(lsp::CompletionResponse::Array(vec![
 9170                task_completion_item,
 9171            ])))
 9172        }
 9173    });
 9174
 9175    cx.condition(|editor, _| editor.context_menu_visible())
 9176        .await;
 9177    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9178    assert!(request.next().await.is_some());
 9179    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9180
 9181    cx.simulate_keystroke("S");
 9182    cx.simulate_keystroke("o");
 9183    cx.simulate_keystroke("m");
 9184    cx.condition(|editor, _| editor.context_menu_visible())
 9185        .await;
 9186    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9187    assert!(request.next().await.is_some());
 9188    assert!(request.next().await.is_some());
 9189    assert!(request.next().await.is_some());
 9190    request.close();
 9191    assert!(request.next().await.is_none());
 9192    assert_eq!(
 9193        counter.load(atomic::Ordering::Acquire),
 9194        4,
 9195        "With the completions menu open, only one LSP request should happen per input"
 9196    );
 9197}
 9198
 9199#[gpui::test]
 9200async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9201    init_test(cx, |_| {});
 9202    let mut cx = EditorTestContext::new(cx).await;
 9203    let language = Arc::new(Language::new(
 9204        LanguageConfig {
 9205            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9206            ..Default::default()
 9207        },
 9208        Some(tree_sitter_rust::LANGUAGE.into()),
 9209    ));
 9210    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9211
 9212    // If multiple selections intersect a line, the line is only toggled once.
 9213    cx.set_state(indoc! {"
 9214        fn a() {
 9215            «//b();
 9216            ˇ»// «c();
 9217            //ˇ»  d();
 9218        }
 9219    "});
 9220
 9221    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9222
 9223    cx.assert_editor_state(indoc! {"
 9224        fn a() {
 9225            «b();
 9226            c();
 9227            ˇ» d();
 9228        }
 9229    "});
 9230
 9231    // The comment prefix is inserted at the same column for every line in a
 9232    // selection.
 9233    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9234
 9235    cx.assert_editor_state(indoc! {"
 9236        fn a() {
 9237            // «b();
 9238            // c();
 9239            ˇ»//  d();
 9240        }
 9241    "});
 9242
 9243    // If a selection ends at the beginning of a line, that line is not toggled.
 9244    cx.set_selections_state(indoc! {"
 9245        fn a() {
 9246            // b();
 9247            «// c();
 9248        ˇ»    //  d();
 9249        }
 9250    "});
 9251
 9252    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9253
 9254    cx.assert_editor_state(indoc! {"
 9255        fn a() {
 9256            // b();
 9257            «c();
 9258        ˇ»    //  d();
 9259        }
 9260    "});
 9261
 9262    // If a selection span a single line and is empty, the line is toggled.
 9263    cx.set_state(indoc! {"
 9264        fn a() {
 9265            a();
 9266            b();
 9267        ˇ
 9268        }
 9269    "});
 9270
 9271    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9272
 9273    cx.assert_editor_state(indoc! {"
 9274        fn a() {
 9275            a();
 9276            b();
 9277        //•ˇ
 9278        }
 9279    "});
 9280
 9281    // If a selection span multiple lines, empty lines are not toggled.
 9282    cx.set_state(indoc! {"
 9283        fn a() {
 9284            «a();
 9285
 9286            c();ˇ»
 9287        }
 9288    "});
 9289
 9290    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9291
 9292    cx.assert_editor_state(indoc! {"
 9293        fn a() {
 9294            // «a();
 9295
 9296            // c();ˇ»
 9297        }
 9298    "});
 9299
 9300    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9301    cx.set_state(indoc! {"
 9302        fn a() {
 9303            «// a();
 9304            /// b();
 9305            //! c();ˇ»
 9306        }
 9307    "});
 9308
 9309    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9310
 9311    cx.assert_editor_state(indoc! {"
 9312        fn a() {
 9313            «a();
 9314            b();
 9315            c();ˇ»
 9316        }
 9317    "});
 9318}
 9319
 9320#[gpui::test]
 9321async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9322    init_test(cx, |_| {});
 9323    let mut cx = EditorTestContext::new(cx).await;
 9324    let language = Arc::new(Language::new(
 9325        LanguageConfig {
 9326            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9327            ..Default::default()
 9328        },
 9329        Some(tree_sitter_rust::LANGUAGE.into()),
 9330    ));
 9331    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9332
 9333    let toggle_comments = &ToggleComments {
 9334        advance_downwards: false,
 9335        ignore_indent: true,
 9336    };
 9337
 9338    // If multiple selections intersect a line, the line is only toggled once.
 9339    cx.set_state(indoc! {"
 9340        fn a() {
 9341        //    «b();
 9342        //    c();
 9343        //    ˇ» d();
 9344        }
 9345    "});
 9346
 9347    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9348
 9349    cx.assert_editor_state(indoc! {"
 9350        fn a() {
 9351            «b();
 9352            c();
 9353            ˇ» d();
 9354        }
 9355    "});
 9356
 9357    // The comment prefix is inserted at the beginning of each line
 9358    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9359
 9360    cx.assert_editor_state(indoc! {"
 9361        fn a() {
 9362        //    «b();
 9363        //    c();
 9364        //    ˇ» d();
 9365        }
 9366    "});
 9367
 9368    // If a selection ends at the beginning of a line, that line is not toggled.
 9369    cx.set_selections_state(indoc! {"
 9370        fn a() {
 9371        //    b();
 9372        //    «c();
 9373        ˇ»//     d();
 9374        }
 9375    "});
 9376
 9377    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9378
 9379    cx.assert_editor_state(indoc! {"
 9380        fn a() {
 9381        //    b();
 9382            «c();
 9383        ˇ»//     d();
 9384        }
 9385    "});
 9386
 9387    // If a selection span a single line and is empty, the line is toggled.
 9388    cx.set_state(indoc! {"
 9389        fn a() {
 9390            a();
 9391            b();
 9392        ˇ
 9393        }
 9394    "});
 9395
 9396    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9397
 9398    cx.assert_editor_state(indoc! {"
 9399        fn a() {
 9400            a();
 9401            b();
 9402        //ˇ
 9403        }
 9404    "});
 9405
 9406    // If a selection span multiple lines, empty lines are not toggled.
 9407    cx.set_state(indoc! {"
 9408        fn a() {
 9409            «a();
 9410
 9411            c();ˇ»
 9412        }
 9413    "});
 9414
 9415    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9416
 9417    cx.assert_editor_state(indoc! {"
 9418        fn a() {
 9419        //    «a();
 9420
 9421        //    c();ˇ»
 9422        }
 9423    "});
 9424
 9425    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9426    cx.set_state(indoc! {"
 9427        fn a() {
 9428        //    «a();
 9429        ///    b();
 9430        //!    c();ˇ»
 9431        }
 9432    "});
 9433
 9434    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9435
 9436    cx.assert_editor_state(indoc! {"
 9437        fn a() {
 9438            «a();
 9439            b();
 9440            c();ˇ»
 9441        }
 9442    "});
 9443}
 9444
 9445#[gpui::test]
 9446async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9447    init_test(cx, |_| {});
 9448
 9449    let language = Arc::new(Language::new(
 9450        LanguageConfig {
 9451            line_comments: vec!["// ".into()],
 9452            ..Default::default()
 9453        },
 9454        Some(tree_sitter_rust::LANGUAGE.into()),
 9455    ));
 9456
 9457    let mut cx = EditorTestContext::new(cx).await;
 9458
 9459    cx.language_registry().add(language.clone());
 9460    cx.update_buffer(|buffer, cx| {
 9461        buffer.set_language(Some(language), cx);
 9462    });
 9463
 9464    let toggle_comments = &ToggleComments {
 9465        advance_downwards: true,
 9466        ignore_indent: false,
 9467    };
 9468
 9469    // Single cursor on one line -> advance
 9470    // Cursor moves horizontally 3 characters as well on non-blank line
 9471    cx.set_state(indoc!(
 9472        "fn a() {
 9473             ˇdog();
 9474             cat();
 9475        }"
 9476    ));
 9477    cx.update_editor(|editor, window, cx| {
 9478        editor.toggle_comments(toggle_comments, window, cx);
 9479    });
 9480    cx.assert_editor_state(indoc!(
 9481        "fn a() {
 9482             // dog();
 9483             catˇ();
 9484        }"
 9485    ));
 9486
 9487    // Single selection on one line -> don't advance
 9488    cx.set_state(indoc!(
 9489        "fn a() {
 9490             «dog()ˇ»;
 9491             cat();
 9492        }"
 9493    ));
 9494    cx.update_editor(|editor, window, cx| {
 9495        editor.toggle_comments(toggle_comments, window, cx);
 9496    });
 9497    cx.assert_editor_state(indoc!(
 9498        "fn a() {
 9499             // «dog()ˇ»;
 9500             cat();
 9501        }"
 9502    ));
 9503
 9504    // Multiple cursors on one line -> advance
 9505    cx.set_state(indoc!(
 9506        "fn a() {
 9507             ˇdˇog();
 9508             cat();
 9509        }"
 9510    ));
 9511    cx.update_editor(|editor, window, cx| {
 9512        editor.toggle_comments(toggle_comments, window, cx);
 9513    });
 9514    cx.assert_editor_state(indoc!(
 9515        "fn a() {
 9516             // dog();
 9517             catˇ(ˇ);
 9518        }"
 9519    ));
 9520
 9521    // Multiple cursors on one line, with selection -> don't advance
 9522    cx.set_state(indoc!(
 9523        "fn a() {
 9524             ˇdˇog«()ˇ»;
 9525             cat();
 9526        }"
 9527    ));
 9528    cx.update_editor(|editor, window, cx| {
 9529        editor.toggle_comments(toggle_comments, window, cx);
 9530    });
 9531    cx.assert_editor_state(indoc!(
 9532        "fn a() {
 9533             // ˇdˇog«()ˇ»;
 9534             cat();
 9535        }"
 9536    ));
 9537
 9538    // Single cursor on one line -> advance
 9539    // Cursor moves to column 0 on blank line
 9540    cx.set_state(indoc!(
 9541        "fn a() {
 9542             ˇdog();
 9543
 9544             cat();
 9545        }"
 9546    ));
 9547    cx.update_editor(|editor, window, cx| {
 9548        editor.toggle_comments(toggle_comments, window, cx);
 9549    });
 9550    cx.assert_editor_state(indoc!(
 9551        "fn a() {
 9552             // dog();
 9553        ˇ
 9554             cat();
 9555        }"
 9556    ));
 9557
 9558    // Single cursor on one line -> advance
 9559    // Cursor starts and ends at column 0
 9560    cx.set_state(indoc!(
 9561        "fn a() {
 9562         ˇ    dog();
 9563             cat();
 9564        }"
 9565    ));
 9566    cx.update_editor(|editor, window, cx| {
 9567        editor.toggle_comments(toggle_comments, window, cx);
 9568    });
 9569    cx.assert_editor_state(indoc!(
 9570        "fn a() {
 9571             // dog();
 9572         ˇ    cat();
 9573        }"
 9574    ));
 9575}
 9576
 9577#[gpui::test]
 9578async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9579    init_test(cx, |_| {});
 9580
 9581    let mut cx = EditorTestContext::new(cx).await;
 9582
 9583    let html_language = Arc::new(
 9584        Language::new(
 9585            LanguageConfig {
 9586                name: "HTML".into(),
 9587                block_comment: Some(("<!-- ".into(), " -->".into())),
 9588                ..Default::default()
 9589            },
 9590            Some(tree_sitter_html::language()),
 9591        )
 9592        .with_injection_query(
 9593            r#"
 9594            (script_element
 9595                (raw_text) @injection.content
 9596                (#set! injection.language "javascript"))
 9597            "#,
 9598        )
 9599        .unwrap(),
 9600    );
 9601
 9602    let javascript_language = Arc::new(Language::new(
 9603        LanguageConfig {
 9604            name: "JavaScript".into(),
 9605            line_comments: vec!["// ".into()],
 9606            ..Default::default()
 9607        },
 9608        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9609    ));
 9610
 9611    cx.language_registry().add(html_language.clone());
 9612    cx.language_registry().add(javascript_language.clone());
 9613    cx.update_buffer(|buffer, cx| {
 9614        buffer.set_language(Some(html_language), cx);
 9615    });
 9616
 9617    // Toggle comments for empty selections
 9618    cx.set_state(
 9619        &r#"
 9620            <p>A</p>ˇ
 9621            <p>B</p>ˇ
 9622            <p>C</p>ˇ
 9623        "#
 9624        .unindent(),
 9625    );
 9626    cx.update_editor(|editor, window, cx| {
 9627        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9628    });
 9629    cx.assert_editor_state(
 9630        &r#"
 9631            <!-- <p>A</p>ˇ -->
 9632            <!-- <p>B</p>ˇ -->
 9633            <!-- <p>C</p>ˇ -->
 9634        "#
 9635        .unindent(),
 9636    );
 9637    cx.update_editor(|editor, window, cx| {
 9638        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9639    });
 9640    cx.assert_editor_state(
 9641        &r#"
 9642            <p>A</p>ˇ
 9643            <p>B</p>ˇ
 9644            <p>C</p>ˇ
 9645        "#
 9646        .unindent(),
 9647    );
 9648
 9649    // Toggle comments for mixture of empty and non-empty selections, where
 9650    // multiple selections occupy a given line.
 9651    cx.set_state(
 9652        &r#"
 9653            <p>A«</p>
 9654            <p>ˇ»B</p>ˇ
 9655            <p>C«</p>
 9656            <p>ˇ»D</p>ˇ
 9657        "#
 9658        .unindent(),
 9659    );
 9660
 9661    cx.update_editor(|editor, window, cx| {
 9662        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9663    });
 9664    cx.assert_editor_state(
 9665        &r#"
 9666            <!-- <p>A«</p>
 9667            <p>ˇ»B</p>ˇ -->
 9668            <!-- <p>C«</p>
 9669            <p>ˇ»D</p>ˇ -->
 9670        "#
 9671        .unindent(),
 9672    );
 9673    cx.update_editor(|editor, window, cx| {
 9674        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9675    });
 9676    cx.assert_editor_state(
 9677        &r#"
 9678            <p>A«</p>
 9679            <p>ˇ»B</p>ˇ
 9680            <p>C«</p>
 9681            <p>ˇ»D</p>ˇ
 9682        "#
 9683        .unindent(),
 9684    );
 9685
 9686    // Toggle comments when different languages are active for different
 9687    // selections.
 9688    cx.set_state(
 9689        &r#"
 9690            ˇ<script>
 9691                ˇvar x = new Y();
 9692            ˇ</script>
 9693        "#
 9694        .unindent(),
 9695    );
 9696    cx.executor().run_until_parked();
 9697    cx.update_editor(|editor, window, cx| {
 9698        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9699    });
 9700    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9701    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9702    cx.assert_editor_state(
 9703        &r#"
 9704            <!-- ˇ<script> -->
 9705                // ˇvar x = new Y();
 9706            // ˇ</script>
 9707        "#
 9708        .unindent(),
 9709    );
 9710}
 9711
 9712#[gpui::test]
 9713fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9714    init_test(cx, |_| {});
 9715
 9716    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9717    let multibuffer = cx.new(|cx| {
 9718        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9719        multibuffer.push_excerpts(
 9720            buffer.clone(),
 9721            [
 9722                ExcerptRange {
 9723                    context: Point::new(0, 0)..Point::new(0, 4),
 9724                    primary: None,
 9725                },
 9726                ExcerptRange {
 9727                    context: Point::new(1, 0)..Point::new(1, 4),
 9728                    primary: None,
 9729                },
 9730            ],
 9731            cx,
 9732        );
 9733        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9734        multibuffer
 9735    });
 9736
 9737    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9738    editor.update_in(cx, |editor, window, cx| {
 9739        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9740        editor.change_selections(None, window, cx, |s| {
 9741            s.select_ranges([
 9742                Point::new(0, 0)..Point::new(0, 0),
 9743                Point::new(1, 0)..Point::new(1, 0),
 9744            ])
 9745        });
 9746
 9747        editor.handle_input("X", window, cx);
 9748        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9749        assert_eq!(
 9750            editor.selections.ranges(cx),
 9751            [
 9752                Point::new(0, 1)..Point::new(0, 1),
 9753                Point::new(1, 1)..Point::new(1, 1),
 9754            ]
 9755        );
 9756
 9757        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9758        editor.change_selections(None, window, cx, |s| {
 9759            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9760        });
 9761        editor.backspace(&Default::default(), window, cx);
 9762        assert_eq!(editor.text(cx), "Xa\nbbb");
 9763        assert_eq!(
 9764            editor.selections.ranges(cx),
 9765            [Point::new(1, 0)..Point::new(1, 0)]
 9766        );
 9767
 9768        editor.change_selections(None, window, cx, |s| {
 9769            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9770        });
 9771        editor.backspace(&Default::default(), window, cx);
 9772        assert_eq!(editor.text(cx), "X\nbb");
 9773        assert_eq!(
 9774            editor.selections.ranges(cx),
 9775            [Point::new(0, 1)..Point::new(0, 1)]
 9776        );
 9777    });
 9778}
 9779
 9780#[gpui::test]
 9781fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9782    init_test(cx, |_| {});
 9783
 9784    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9785    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9786        indoc! {"
 9787            [aaaa
 9788            (bbbb]
 9789            cccc)",
 9790        },
 9791        markers.clone(),
 9792    );
 9793    let excerpt_ranges = markers.into_iter().map(|marker| {
 9794        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9795        ExcerptRange {
 9796            context,
 9797            primary: None,
 9798        }
 9799    });
 9800    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9801    let multibuffer = cx.new(|cx| {
 9802        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9803        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9804        multibuffer
 9805    });
 9806
 9807    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9808    editor.update_in(cx, |editor, window, cx| {
 9809        let (expected_text, selection_ranges) = marked_text_ranges(
 9810            indoc! {"
 9811                aaaa
 9812                bˇbbb
 9813                bˇbbˇb
 9814                cccc"
 9815            },
 9816            true,
 9817        );
 9818        assert_eq!(editor.text(cx), expected_text);
 9819        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9820
 9821        editor.handle_input("X", window, cx);
 9822
 9823        let (expected_text, expected_selections) = marked_text_ranges(
 9824            indoc! {"
 9825                aaaa
 9826                bXˇbbXb
 9827                bXˇbbXˇb
 9828                cccc"
 9829            },
 9830            false,
 9831        );
 9832        assert_eq!(editor.text(cx), expected_text);
 9833        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9834
 9835        editor.newline(&Newline, window, cx);
 9836        let (expected_text, expected_selections) = marked_text_ranges(
 9837            indoc! {"
 9838                aaaa
 9839                bX
 9840                ˇbbX
 9841                b
 9842                bX
 9843                ˇbbX
 9844                ˇb
 9845                cccc"
 9846            },
 9847            false,
 9848        );
 9849        assert_eq!(editor.text(cx), expected_text);
 9850        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9851    });
 9852}
 9853
 9854#[gpui::test]
 9855fn test_refresh_selections(cx: &mut TestAppContext) {
 9856    init_test(cx, |_| {});
 9857
 9858    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9859    let mut excerpt1_id = None;
 9860    let multibuffer = cx.new(|cx| {
 9861        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9862        excerpt1_id = multibuffer
 9863            .push_excerpts(
 9864                buffer.clone(),
 9865                [
 9866                    ExcerptRange {
 9867                        context: Point::new(0, 0)..Point::new(1, 4),
 9868                        primary: None,
 9869                    },
 9870                    ExcerptRange {
 9871                        context: Point::new(1, 0)..Point::new(2, 4),
 9872                        primary: None,
 9873                    },
 9874                ],
 9875                cx,
 9876            )
 9877            .into_iter()
 9878            .next();
 9879        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9880        multibuffer
 9881    });
 9882
 9883    let editor = cx.add_window(|window, cx| {
 9884        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9885        let snapshot = editor.snapshot(window, cx);
 9886        editor.change_selections(None, window, cx, |s| {
 9887            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9888        });
 9889        editor.begin_selection(
 9890            Point::new(2, 1).to_display_point(&snapshot),
 9891            true,
 9892            1,
 9893            window,
 9894            cx,
 9895        );
 9896        assert_eq!(
 9897            editor.selections.ranges(cx),
 9898            [
 9899                Point::new(1, 3)..Point::new(1, 3),
 9900                Point::new(2, 1)..Point::new(2, 1),
 9901            ]
 9902        );
 9903        editor
 9904    });
 9905
 9906    // Refreshing selections is a no-op when excerpts haven't changed.
 9907    _ = editor.update(cx, |editor, window, cx| {
 9908        editor.change_selections(None, window, cx, |s| s.refresh());
 9909        assert_eq!(
 9910            editor.selections.ranges(cx),
 9911            [
 9912                Point::new(1, 3)..Point::new(1, 3),
 9913                Point::new(2, 1)..Point::new(2, 1),
 9914            ]
 9915        );
 9916    });
 9917
 9918    multibuffer.update(cx, |multibuffer, cx| {
 9919        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9920    });
 9921    _ = editor.update(cx, |editor, window, cx| {
 9922        // Removing an excerpt causes the first selection to become degenerate.
 9923        assert_eq!(
 9924            editor.selections.ranges(cx),
 9925            [
 9926                Point::new(0, 0)..Point::new(0, 0),
 9927                Point::new(0, 1)..Point::new(0, 1)
 9928            ]
 9929        );
 9930
 9931        // Refreshing selections will relocate the first selection to the original buffer
 9932        // location.
 9933        editor.change_selections(None, window, cx, |s| s.refresh());
 9934        assert_eq!(
 9935            editor.selections.ranges(cx),
 9936            [
 9937                Point::new(0, 1)..Point::new(0, 1),
 9938                Point::new(0, 3)..Point::new(0, 3)
 9939            ]
 9940        );
 9941        assert!(editor.selections.pending_anchor().is_some());
 9942    });
 9943}
 9944
 9945#[gpui::test]
 9946fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9947    init_test(cx, |_| {});
 9948
 9949    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9950    let mut excerpt1_id = None;
 9951    let multibuffer = cx.new(|cx| {
 9952        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9953        excerpt1_id = multibuffer
 9954            .push_excerpts(
 9955                buffer.clone(),
 9956                [
 9957                    ExcerptRange {
 9958                        context: Point::new(0, 0)..Point::new(1, 4),
 9959                        primary: None,
 9960                    },
 9961                    ExcerptRange {
 9962                        context: Point::new(1, 0)..Point::new(2, 4),
 9963                        primary: None,
 9964                    },
 9965                ],
 9966                cx,
 9967            )
 9968            .into_iter()
 9969            .next();
 9970        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9971        multibuffer
 9972    });
 9973
 9974    let editor = cx.add_window(|window, cx| {
 9975        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9976        let snapshot = editor.snapshot(window, cx);
 9977        editor.begin_selection(
 9978            Point::new(1, 3).to_display_point(&snapshot),
 9979            false,
 9980            1,
 9981            window,
 9982            cx,
 9983        );
 9984        assert_eq!(
 9985            editor.selections.ranges(cx),
 9986            [Point::new(1, 3)..Point::new(1, 3)]
 9987        );
 9988        editor
 9989    });
 9990
 9991    multibuffer.update(cx, |multibuffer, cx| {
 9992        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9993    });
 9994    _ = editor.update(cx, |editor, window, cx| {
 9995        assert_eq!(
 9996            editor.selections.ranges(cx),
 9997            [Point::new(0, 0)..Point::new(0, 0)]
 9998        );
 9999
10000        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10001        editor.change_selections(None, window, cx, |s| s.refresh());
10002        assert_eq!(
10003            editor.selections.ranges(cx),
10004            [Point::new(0, 3)..Point::new(0, 3)]
10005        );
10006        assert!(editor.selections.pending_anchor().is_some());
10007    });
10008}
10009
10010#[gpui::test]
10011async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10012    init_test(cx, |_| {});
10013
10014    let language = Arc::new(
10015        Language::new(
10016            LanguageConfig {
10017                brackets: BracketPairConfig {
10018                    pairs: vec![
10019                        BracketPair {
10020                            start: "{".to_string(),
10021                            end: "}".to_string(),
10022                            close: true,
10023                            surround: true,
10024                            newline: true,
10025                        },
10026                        BracketPair {
10027                            start: "/* ".to_string(),
10028                            end: " */".to_string(),
10029                            close: true,
10030                            surround: true,
10031                            newline: true,
10032                        },
10033                    ],
10034                    ..Default::default()
10035                },
10036                ..Default::default()
10037            },
10038            Some(tree_sitter_rust::LANGUAGE.into()),
10039        )
10040        .with_indents_query("")
10041        .unwrap(),
10042    );
10043
10044    let text = concat!(
10045        "{   }\n",     //
10046        "  x\n",       //
10047        "  /*   */\n", //
10048        "x\n",         //
10049        "{{} }\n",     //
10050    );
10051
10052    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10053    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10054    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10055    editor
10056        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10057        .await;
10058
10059    editor.update_in(cx, |editor, window, cx| {
10060        editor.change_selections(None, window, cx, |s| {
10061            s.select_display_ranges([
10062                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10063                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10064                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10065            ])
10066        });
10067        editor.newline(&Newline, window, cx);
10068
10069        assert_eq!(
10070            editor.buffer().read(cx).read(cx).text(),
10071            concat!(
10072                "{ \n",    // Suppress rustfmt
10073                "\n",      //
10074                "}\n",     //
10075                "  x\n",   //
10076                "  /* \n", //
10077                "  \n",    //
10078                "  */\n",  //
10079                "x\n",     //
10080                "{{} \n",  //
10081                "}\n",     //
10082            )
10083        );
10084    });
10085}
10086
10087#[gpui::test]
10088fn test_highlighted_ranges(cx: &mut TestAppContext) {
10089    init_test(cx, |_| {});
10090
10091    let editor = cx.add_window(|window, cx| {
10092        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10093        build_editor(buffer.clone(), window, cx)
10094    });
10095
10096    _ = editor.update(cx, |editor, window, cx| {
10097        struct Type1;
10098        struct Type2;
10099
10100        let buffer = editor.buffer.read(cx).snapshot(cx);
10101
10102        let anchor_range =
10103            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10104
10105        editor.highlight_background::<Type1>(
10106            &[
10107                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10108                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10109                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10110                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10111            ],
10112            |_| Hsla::red(),
10113            cx,
10114        );
10115        editor.highlight_background::<Type2>(
10116            &[
10117                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10118                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10119                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10120                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10121            ],
10122            |_| Hsla::green(),
10123            cx,
10124        );
10125
10126        let snapshot = editor.snapshot(window, cx);
10127        let mut highlighted_ranges = editor.background_highlights_in_range(
10128            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10129            &snapshot,
10130            cx.theme().colors(),
10131        );
10132        // Enforce a consistent ordering based on color without relying on the ordering of the
10133        // highlight's `TypeId` which is non-executor.
10134        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10135        assert_eq!(
10136            highlighted_ranges,
10137            &[
10138                (
10139                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10140                    Hsla::red(),
10141                ),
10142                (
10143                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10144                    Hsla::red(),
10145                ),
10146                (
10147                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10148                    Hsla::green(),
10149                ),
10150                (
10151                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10152                    Hsla::green(),
10153                ),
10154            ]
10155        );
10156        assert_eq!(
10157            editor.background_highlights_in_range(
10158                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10159                &snapshot,
10160                cx.theme().colors(),
10161            ),
10162            &[(
10163                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10164                Hsla::red(),
10165            )]
10166        );
10167    });
10168}
10169
10170#[gpui::test]
10171async fn test_following(cx: &mut gpui::TestAppContext) {
10172    init_test(cx, |_| {});
10173
10174    let fs = FakeFs::new(cx.executor());
10175    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10176
10177    let buffer = project.update(cx, |project, cx| {
10178        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10179        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10180    });
10181    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10182    let follower = cx.update(|cx| {
10183        cx.open_window(
10184            WindowOptions {
10185                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10186                    gpui::Point::new(px(0.), px(0.)),
10187                    gpui::Point::new(px(10.), px(80.)),
10188                ))),
10189                ..Default::default()
10190            },
10191            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10192        )
10193        .unwrap()
10194    });
10195
10196    let is_still_following = Rc::new(RefCell::new(true));
10197    let follower_edit_event_count = Rc::new(RefCell::new(0));
10198    let pending_update = Rc::new(RefCell::new(None));
10199    let leader_entity = leader.root(cx).unwrap();
10200    let follower_entity = follower.root(cx).unwrap();
10201    _ = follower.update(cx, {
10202        let update = pending_update.clone();
10203        let is_still_following = is_still_following.clone();
10204        let follower_edit_event_count = follower_edit_event_count.clone();
10205        |_, window, cx| {
10206            cx.subscribe_in(
10207                &leader_entity,
10208                window,
10209                move |_, leader, event, window, cx| {
10210                    leader.read(cx).add_event_to_update_proto(
10211                        event,
10212                        &mut update.borrow_mut(),
10213                        window,
10214                        cx,
10215                    );
10216                },
10217            )
10218            .detach();
10219
10220            cx.subscribe_in(
10221                &follower_entity,
10222                window,
10223                move |_, _, event: &EditorEvent, _window, _cx| {
10224                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10225                        *is_still_following.borrow_mut() = false;
10226                    }
10227
10228                    if let EditorEvent::BufferEdited = event {
10229                        *follower_edit_event_count.borrow_mut() += 1;
10230                    }
10231                },
10232            )
10233            .detach();
10234        }
10235    });
10236
10237    // Update the selections only
10238    _ = leader.update(cx, |leader, window, cx| {
10239        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10240    });
10241    follower
10242        .update(cx, |follower, window, cx| {
10243            follower.apply_update_proto(
10244                &project,
10245                pending_update.borrow_mut().take().unwrap(),
10246                window,
10247                cx,
10248            )
10249        })
10250        .unwrap()
10251        .await
10252        .unwrap();
10253    _ = follower.update(cx, |follower, _, cx| {
10254        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10255    });
10256    assert!(*is_still_following.borrow());
10257    assert_eq!(*follower_edit_event_count.borrow(), 0);
10258
10259    // Update the scroll position only
10260    _ = leader.update(cx, |leader, window, cx| {
10261        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10262    });
10263    follower
10264        .update(cx, |follower, window, cx| {
10265            follower.apply_update_proto(
10266                &project,
10267                pending_update.borrow_mut().take().unwrap(),
10268                window,
10269                cx,
10270            )
10271        })
10272        .unwrap()
10273        .await
10274        .unwrap();
10275    assert_eq!(
10276        follower
10277            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10278            .unwrap(),
10279        gpui::Point::new(1.5, 3.5)
10280    );
10281    assert!(*is_still_following.borrow());
10282    assert_eq!(*follower_edit_event_count.borrow(), 0);
10283
10284    // Update the selections and scroll position. The follower's scroll position is updated
10285    // via autoscroll, not via the leader's exact scroll position.
10286    _ = leader.update(cx, |leader, window, cx| {
10287        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10288        leader.request_autoscroll(Autoscroll::newest(), cx);
10289        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10290    });
10291    follower
10292        .update(cx, |follower, window, cx| {
10293            follower.apply_update_proto(
10294                &project,
10295                pending_update.borrow_mut().take().unwrap(),
10296                window,
10297                cx,
10298            )
10299        })
10300        .unwrap()
10301        .await
10302        .unwrap();
10303    _ = follower.update(cx, |follower, _, cx| {
10304        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10305        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10306    });
10307    assert!(*is_still_following.borrow());
10308
10309    // Creating a pending selection that precedes another selection
10310    _ = leader.update(cx, |leader, window, cx| {
10311        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10312        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10313    });
10314    follower
10315        .update(cx, |follower, window, cx| {
10316            follower.apply_update_proto(
10317                &project,
10318                pending_update.borrow_mut().take().unwrap(),
10319                window,
10320                cx,
10321            )
10322        })
10323        .unwrap()
10324        .await
10325        .unwrap();
10326    _ = follower.update(cx, |follower, _, cx| {
10327        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10328    });
10329    assert!(*is_still_following.borrow());
10330
10331    // Extend the pending selection so that it surrounds another selection
10332    _ = leader.update(cx, |leader, window, cx| {
10333        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10334    });
10335    follower
10336        .update(cx, |follower, window, cx| {
10337            follower.apply_update_proto(
10338                &project,
10339                pending_update.borrow_mut().take().unwrap(),
10340                window,
10341                cx,
10342            )
10343        })
10344        .unwrap()
10345        .await
10346        .unwrap();
10347    _ = follower.update(cx, |follower, _, cx| {
10348        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10349    });
10350
10351    // Scrolling locally breaks the follow
10352    _ = follower.update(cx, |follower, window, cx| {
10353        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10354        follower.set_scroll_anchor(
10355            ScrollAnchor {
10356                anchor: top_anchor,
10357                offset: gpui::Point::new(0.0, 0.5),
10358            },
10359            window,
10360            cx,
10361        );
10362    });
10363    assert!(!(*is_still_following.borrow()));
10364}
10365
10366#[gpui::test]
10367async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10368    init_test(cx, |_| {});
10369
10370    let fs = FakeFs::new(cx.executor());
10371    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10372    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10373    let pane = workspace
10374        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10375        .unwrap();
10376
10377    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10378
10379    let leader = pane.update_in(cx, |_, window, cx| {
10380        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10381        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10382    });
10383
10384    // Start following the editor when it has no excerpts.
10385    let mut state_message =
10386        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10387    let workspace_entity = workspace.root(cx).unwrap();
10388    let follower_1 = cx
10389        .update_window(*workspace.deref(), |_, window, cx| {
10390            Editor::from_state_proto(
10391                workspace_entity,
10392                ViewId {
10393                    creator: Default::default(),
10394                    id: 0,
10395                },
10396                &mut state_message,
10397                window,
10398                cx,
10399            )
10400        })
10401        .unwrap()
10402        .unwrap()
10403        .await
10404        .unwrap();
10405
10406    let update_message = Rc::new(RefCell::new(None));
10407    follower_1.update_in(cx, {
10408        let update = update_message.clone();
10409        |_, window, cx| {
10410            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10411                leader.read(cx).add_event_to_update_proto(
10412                    event,
10413                    &mut update.borrow_mut(),
10414                    window,
10415                    cx,
10416                );
10417            })
10418            .detach();
10419        }
10420    });
10421
10422    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10423        (
10424            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10425            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10426        )
10427    });
10428
10429    // Insert some excerpts.
10430    leader.update(cx, |leader, cx| {
10431        leader.buffer.update(cx, |multibuffer, cx| {
10432            let excerpt_ids = multibuffer.push_excerpts(
10433                buffer_1.clone(),
10434                [
10435                    ExcerptRange {
10436                        context: 1..6,
10437                        primary: None,
10438                    },
10439                    ExcerptRange {
10440                        context: 12..15,
10441                        primary: None,
10442                    },
10443                    ExcerptRange {
10444                        context: 0..3,
10445                        primary: None,
10446                    },
10447                ],
10448                cx,
10449            );
10450            multibuffer.insert_excerpts_after(
10451                excerpt_ids[0],
10452                buffer_2.clone(),
10453                [
10454                    ExcerptRange {
10455                        context: 8..12,
10456                        primary: None,
10457                    },
10458                    ExcerptRange {
10459                        context: 0..6,
10460                        primary: None,
10461                    },
10462                ],
10463                cx,
10464            );
10465        });
10466    });
10467
10468    // Apply the update of adding the excerpts.
10469    follower_1
10470        .update_in(cx, |follower, window, cx| {
10471            follower.apply_update_proto(
10472                &project,
10473                update_message.borrow().clone().unwrap(),
10474                window,
10475                cx,
10476            )
10477        })
10478        .await
10479        .unwrap();
10480    assert_eq!(
10481        follower_1.update(cx, |editor, cx| editor.text(cx)),
10482        leader.update(cx, |editor, cx| editor.text(cx))
10483    );
10484    update_message.borrow_mut().take();
10485
10486    // Start following separately after it already has excerpts.
10487    let mut state_message =
10488        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10489    let workspace_entity = workspace.root(cx).unwrap();
10490    let follower_2 = cx
10491        .update_window(*workspace.deref(), |_, window, cx| {
10492            Editor::from_state_proto(
10493                workspace_entity,
10494                ViewId {
10495                    creator: Default::default(),
10496                    id: 0,
10497                },
10498                &mut state_message,
10499                window,
10500                cx,
10501            )
10502        })
10503        .unwrap()
10504        .unwrap()
10505        .await
10506        .unwrap();
10507    assert_eq!(
10508        follower_2.update(cx, |editor, cx| editor.text(cx)),
10509        leader.update(cx, |editor, cx| editor.text(cx))
10510    );
10511
10512    // Remove some excerpts.
10513    leader.update(cx, |leader, cx| {
10514        leader.buffer.update(cx, |multibuffer, cx| {
10515            let excerpt_ids = multibuffer.excerpt_ids();
10516            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10517            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10518        });
10519    });
10520
10521    // Apply the update of removing the excerpts.
10522    follower_1
10523        .update_in(cx, |follower, window, cx| {
10524            follower.apply_update_proto(
10525                &project,
10526                update_message.borrow().clone().unwrap(),
10527                window,
10528                cx,
10529            )
10530        })
10531        .await
10532        .unwrap();
10533    follower_2
10534        .update_in(cx, |follower, window, cx| {
10535            follower.apply_update_proto(
10536                &project,
10537                update_message.borrow().clone().unwrap(),
10538                window,
10539                cx,
10540            )
10541        })
10542        .await
10543        .unwrap();
10544    update_message.borrow_mut().take();
10545    assert_eq!(
10546        follower_1.update(cx, |editor, cx| editor.text(cx)),
10547        leader.update(cx, |editor, cx| editor.text(cx))
10548    );
10549}
10550
10551#[gpui::test]
10552async fn go_to_prev_overlapping_diagnostic(
10553    executor: BackgroundExecutor,
10554    cx: &mut gpui::TestAppContext,
10555) {
10556    init_test(cx, |_| {});
10557
10558    let mut cx = EditorTestContext::new(cx).await;
10559    let lsp_store =
10560        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10561
10562    cx.set_state(indoc! {"
10563        ˇfn func(abc def: i32) -> u32 {
10564        }
10565    "});
10566
10567    cx.update(|_, cx| {
10568        lsp_store.update(cx, |lsp_store, cx| {
10569            lsp_store
10570                .update_diagnostics(
10571                    LanguageServerId(0),
10572                    lsp::PublishDiagnosticsParams {
10573                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
10574                        version: None,
10575                        diagnostics: vec![
10576                            lsp::Diagnostic {
10577                                range: lsp::Range::new(
10578                                    lsp::Position::new(0, 11),
10579                                    lsp::Position::new(0, 12),
10580                                ),
10581                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10582                                ..Default::default()
10583                            },
10584                            lsp::Diagnostic {
10585                                range: lsp::Range::new(
10586                                    lsp::Position::new(0, 12),
10587                                    lsp::Position::new(0, 15),
10588                                ),
10589                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10590                                ..Default::default()
10591                            },
10592                            lsp::Diagnostic {
10593                                range: lsp::Range::new(
10594                                    lsp::Position::new(0, 25),
10595                                    lsp::Position::new(0, 28),
10596                                ),
10597                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10598                                ..Default::default()
10599                            },
10600                        ],
10601                    },
10602                    &[],
10603                    cx,
10604                )
10605                .unwrap()
10606        });
10607    });
10608
10609    executor.run_until_parked();
10610
10611    cx.update_editor(|editor, window, cx| {
10612        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10613    });
10614
10615    cx.assert_editor_state(indoc! {"
10616        fn func(abc def: i32) -> ˇu32 {
10617        }
10618    "});
10619
10620    cx.update_editor(|editor, window, cx| {
10621        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10622    });
10623
10624    cx.assert_editor_state(indoc! {"
10625        fn func(abc ˇdef: i32) -> u32 {
10626        }
10627    "});
10628
10629    cx.update_editor(|editor, window, cx| {
10630        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10631    });
10632
10633    cx.assert_editor_state(indoc! {"
10634        fn func(abcˇ def: i32) -> u32 {
10635        }
10636    "});
10637
10638    cx.update_editor(|editor, window, cx| {
10639        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10640    });
10641
10642    cx.assert_editor_state(indoc! {"
10643        fn func(abc def: i32) -> ˇu32 {
10644        }
10645    "});
10646}
10647
10648#[gpui::test]
10649async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10650    init_test(cx, |_| {});
10651
10652    let mut cx = EditorTestContext::new(cx).await;
10653
10654    cx.set_state(indoc! {"
10655        fn func(abˇc def: i32) -> u32 {
10656        }
10657    "});
10658    let lsp_store =
10659        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10660
10661    cx.update(|_, cx| {
10662        lsp_store.update(cx, |lsp_store, cx| {
10663            lsp_store.update_diagnostics(
10664                LanguageServerId(0),
10665                lsp::PublishDiagnosticsParams {
10666                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10667                    version: None,
10668                    diagnostics: vec![lsp::Diagnostic {
10669                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10670                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10671                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10672                        ..Default::default()
10673                    }],
10674                },
10675                &[],
10676                cx,
10677            )
10678        })
10679    }).unwrap();
10680    cx.run_until_parked();
10681    cx.update_editor(|editor, window, cx| {
10682        hover_popover::hover(editor, &Default::default(), window, cx)
10683    });
10684    cx.run_until_parked();
10685    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10686}
10687
10688#[gpui::test]
10689async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10690    init_test(cx, |_| {});
10691
10692    let mut cx = EditorTestContext::new(cx).await;
10693
10694    let diff_base = r#"
10695        use some::mod;
10696
10697        const A: u32 = 42;
10698
10699        fn main() {
10700            println!("hello");
10701
10702            println!("world");
10703        }
10704        "#
10705    .unindent();
10706
10707    // Edits are modified, removed, modified, added
10708    cx.set_state(
10709        &r#"
10710        use some::modified;
10711
10712        ˇ
10713        fn main() {
10714            println!("hello there");
10715
10716            println!("around the");
10717            println!("world");
10718        }
10719        "#
10720        .unindent(),
10721    );
10722
10723    cx.set_diff_base(&diff_base);
10724    executor.run_until_parked();
10725
10726    cx.update_editor(|editor, window, cx| {
10727        //Wrap around the bottom of the buffer
10728        for _ in 0..3 {
10729            editor.go_to_next_hunk(&GoToHunk, window, cx);
10730        }
10731    });
10732
10733    cx.assert_editor_state(
10734        &r#"
10735        ˇuse some::modified;
10736
10737
10738        fn main() {
10739            println!("hello there");
10740
10741            println!("around the");
10742            println!("world");
10743        }
10744        "#
10745        .unindent(),
10746    );
10747
10748    cx.update_editor(|editor, window, cx| {
10749        //Wrap around the top of the buffer
10750        for _ in 0..2 {
10751            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10752        }
10753    });
10754
10755    cx.assert_editor_state(
10756        &r#"
10757        use some::modified;
10758
10759
10760        fn main() {
10761        ˇ    println!("hello there");
10762
10763            println!("around the");
10764            println!("world");
10765        }
10766        "#
10767        .unindent(),
10768    );
10769
10770    cx.update_editor(|editor, window, cx| {
10771        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10772    });
10773
10774    cx.assert_editor_state(
10775        &r#"
10776        use some::modified;
10777
10778        ˇ
10779        fn main() {
10780            println!("hello there");
10781
10782            println!("around the");
10783            println!("world");
10784        }
10785        "#
10786        .unindent(),
10787    );
10788
10789    cx.update_editor(|editor, window, cx| {
10790        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10791    });
10792
10793    cx.assert_editor_state(
10794        &r#"
10795        ˇuse some::modified;
10796
10797
10798        fn main() {
10799            println!("hello there");
10800
10801            println!("around the");
10802            println!("world");
10803        }
10804        "#
10805        .unindent(),
10806    );
10807
10808    cx.update_editor(|editor, window, cx| {
10809        for _ in 0..2 {
10810            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10811        }
10812    });
10813
10814    cx.assert_editor_state(
10815        &r#"
10816        use some::modified;
10817
10818
10819        fn main() {
10820        ˇ    println!("hello there");
10821
10822            println!("around the");
10823            println!("world");
10824        }
10825        "#
10826        .unindent(),
10827    );
10828
10829    cx.update_editor(|editor, window, cx| {
10830        editor.fold(&Fold, window, cx);
10831    });
10832
10833    cx.update_editor(|editor, window, cx| {
10834        editor.go_to_next_hunk(&GoToHunk, window, cx);
10835    });
10836
10837    cx.assert_editor_state(
10838        &r#"
10839        ˇuse some::modified;
10840
10841
10842        fn main() {
10843            println!("hello there");
10844
10845            println!("around the");
10846            println!("world");
10847        }
10848        "#
10849        .unindent(),
10850    );
10851}
10852
10853#[test]
10854fn test_split_words() {
10855    fn split(text: &str) -> Vec<&str> {
10856        split_words(text).collect()
10857    }
10858
10859    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10860    assert_eq!(split("hello_world"), &["hello_", "world"]);
10861    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10862    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10863    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10864    assert_eq!(split("helloworld"), &["helloworld"]);
10865
10866    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10867}
10868
10869#[gpui::test]
10870async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10871    init_test(cx, |_| {});
10872
10873    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10874    let mut assert = |before, after| {
10875        let _state_context = cx.set_state(before);
10876        cx.run_until_parked();
10877        cx.update_editor(|editor, window, cx| {
10878            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
10879        });
10880        cx.assert_editor_state(after);
10881    };
10882
10883    // Outside bracket jumps to outside of matching bracket
10884    assert("console.logˇ(var);", "console.log(var)ˇ;");
10885    assert("console.log(var)ˇ;", "console.logˇ(var);");
10886
10887    // Inside bracket jumps to inside of matching bracket
10888    assert("console.log(ˇvar);", "console.log(varˇ);");
10889    assert("console.log(varˇ);", "console.log(ˇvar);");
10890
10891    // When outside a bracket and inside, favor jumping to the inside bracket
10892    assert(
10893        "console.log('foo', [1, 2, 3]ˇ);",
10894        "console.log(ˇ'foo', [1, 2, 3]);",
10895    );
10896    assert(
10897        "console.log(ˇ'foo', [1, 2, 3]);",
10898        "console.log('foo', [1, 2, 3]ˇ);",
10899    );
10900
10901    // Bias forward if two options are equally likely
10902    assert(
10903        "let result = curried_fun()ˇ();",
10904        "let result = curried_fun()()ˇ;",
10905    );
10906
10907    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10908    assert(
10909        indoc! {"
10910            function test() {
10911                console.log('test')ˇ
10912            }"},
10913        indoc! {"
10914            function test() {
10915                console.logˇ('test')
10916            }"},
10917    );
10918}
10919
10920#[gpui::test]
10921async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10922    init_test(cx, |_| {});
10923
10924    let fs = FakeFs::new(cx.executor());
10925    fs.insert_tree(
10926        "/a",
10927        json!({
10928            "main.rs": "fn main() { let a = 5; }",
10929            "other.rs": "// Test file",
10930        }),
10931    )
10932    .await;
10933    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10934
10935    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10936    language_registry.add(Arc::new(Language::new(
10937        LanguageConfig {
10938            name: "Rust".into(),
10939            matcher: LanguageMatcher {
10940                path_suffixes: vec!["rs".to_string()],
10941                ..Default::default()
10942            },
10943            brackets: BracketPairConfig {
10944                pairs: vec![BracketPair {
10945                    start: "{".to_string(),
10946                    end: "}".to_string(),
10947                    close: true,
10948                    surround: true,
10949                    newline: true,
10950                }],
10951                disabled_scopes_by_bracket_ix: Vec::new(),
10952            },
10953            ..Default::default()
10954        },
10955        Some(tree_sitter_rust::LANGUAGE.into()),
10956    )));
10957    let mut fake_servers = language_registry.register_fake_lsp(
10958        "Rust",
10959        FakeLspAdapter {
10960            capabilities: lsp::ServerCapabilities {
10961                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10962                    first_trigger_character: "{".to_string(),
10963                    more_trigger_character: None,
10964                }),
10965                ..Default::default()
10966            },
10967            ..Default::default()
10968        },
10969    );
10970
10971    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10972
10973    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10974
10975    let worktree_id = workspace
10976        .update(cx, |workspace, _, cx| {
10977            workspace.project().update(cx, |project, cx| {
10978                project.worktrees(cx).next().unwrap().read(cx).id()
10979            })
10980        })
10981        .unwrap();
10982
10983    let buffer = project
10984        .update(cx, |project, cx| {
10985            project.open_local_buffer("/a/main.rs", cx)
10986        })
10987        .await
10988        .unwrap();
10989    let editor_handle = workspace
10990        .update(cx, |workspace, window, cx| {
10991            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
10992        })
10993        .unwrap()
10994        .await
10995        .unwrap()
10996        .downcast::<Editor>()
10997        .unwrap();
10998
10999    cx.executor().start_waiting();
11000    let fake_server = fake_servers.next().await.unwrap();
11001
11002    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11003        assert_eq!(
11004            params.text_document_position.text_document.uri,
11005            lsp::Url::from_file_path("/a/main.rs").unwrap(),
11006        );
11007        assert_eq!(
11008            params.text_document_position.position,
11009            lsp::Position::new(0, 21),
11010        );
11011
11012        Ok(Some(vec![lsp::TextEdit {
11013            new_text: "]".to_string(),
11014            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11015        }]))
11016    });
11017
11018    editor_handle.update_in(cx, |editor, window, cx| {
11019        window.focus(&editor.focus_handle(cx));
11020        editor.change_selections(None, window, cx, |s| {
11021            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11022        });
11023        editor.handle_input("{", window, cx);
11024    });
11025
11026    cx.executor().run_until_parked();
11027
11028    buffer.update(cx, |buffer, _| {
11029        assert_eq!(
11030            buffer.text(),
11031            "fn main() { let a = {5}; }",
11032            "No extra braces from on type formatting should appear in the buffer"
11033        )
11034    });
11035}
11036
11037#[gpui::test]
11038async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11039    init_test(cx, |_| {});
11040
11041    let fs = FakeFs::new(cx.executor());
11042    fs.insert_tree(
11043        "/a",
11044        json!({
11045            "main.rs": "fn main() { let a = 5; }",
11046            "other.rs": "// Test file",
11047        }),
11048    )
11049    .await;
11050
11051    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11052
11053    let server_restarts = Arc::new(AtomicUsize::new(0));
11054    let closure_restarts = Arc::clone(&server_restarts);
11055    let language_server_name = "test language server";
11056    let language_name: LanguageName = "Rust".into();
11057
11058    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11059    language_registry.add(Arc::new(Language::new(
11060        LanguageConfig {
11061            name: language_name.clone(),
11062            matcher: LanguageMatcher {
11063                path_suffixes: vec!["rs".to_string()],
11064                ..Default::default()
11065            },
11066            ..Default::default()
11067        },
11068        Some(tree_sitter_rust::LANGUAGE.into()),
11069    )));
11070    let mut fake_servers = language_registry.register_fake_lsp(
11071        "Rust",
11072        FakeLspAdapter {
11073            name: language_server_name,
11074            initialization_options: Some(json!({
11075                "testOptionValue": true
11076            })),
11077            initializer: Some(Box::new(move |fake_server| {
11078                let task_restarts = Arc::clone(&closure_restarts);
11079                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11080                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11081                    futures::future::ready(Ok(()))
11082                });
11083            })),
11084            ..Default::default()
11085        },
11086    );
11087
11088    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11089    let _buffer = project
11090        .update(cx, |project, cx| {
11091            project.open_local_buffer_with_lsp("/a/main.rs", cx)
11092        })
11093        .await
11094        .unwrap();
11095    let _fake_server = fake_servers.next().await.unwrap();
11096    update_test_language_settings(cx, |language_settings| {
11097        language_settings.languages.insert(
11098            language_name.clone(),
11099            LanguageSettingsContent {
11100                tab_size: NonZeroU32::new(8),
11101                ..Default::default()
11102            },
11103        );
11104    });
11105    cx.executor().run_until_parked();
11106    assert_eq!(
11107        server_restarts.load(atomic::Ordering::Acquire),
11108        0,
11109        "Should not restart LSP server on an unrelated change"
11110    );
11111
11112    update_test_project_settings(cx, |project_settings| {
11113        project_settings.lsp.insert(
11114            "Some other server name".into(),
11115            LspSettings {
11116                binary: None,
11117                settings: None,
11118                initialization_options: Some(json!({
11119                    "some other init value": false
11120                })),
11121            },
11122        );
11123    });
11124    cx.executor().run_until_parked();
11125    assert_eq!(
11126        server_restarts.load(atomic::Ordering::Acquire),
11127        0,
11128        "Should not restart LSP server on an unrelated LSP settings change"
11129    );
11130
11131    update_test_project_settings(cx, |project_settings| {
11132        project_settings.lsp.insert(
11133            language_server_name.into(),
11134            LspSettings {
11135                binary: None,
11136                settings: None,
11137                initialization_options: Some(json!({
11138                    "anotherInitValue": false
11139                })),
11140            },
11141        );
11142    });
11143    cx.executor().run_until_parked();
11144    assert_eq!(
11145        server_restarts.load(atomic::Ordering::Acquire),
11146        1,
11147        "Should restart LSP server on a related LSP settings change"
11148    );
11149
11150    update_test_project_settings(cx, |project_settings| {
11151        project_settings.lsp.insert(
11152            language_server_name.into(),
11153            LspSettings {
11154                binary: None,
11155                settings: None,
11156                initialization_options: Some(json!({
11157                    "anotherInitValue": false
11158                })),
11159            },
11160        );
11161    });
11162    cx.executor().run_until_parked();
11163    assert_eq!(
11164        server_restarts.load(atomic::Ordering::Acquire),
11165        1,
11166        "Should not restart LSP server on a related LSP settings change that is the same"
11167    );
11168
11169    update_test_project_settings(cx, |project_settings| {
11170        project_settings.lsp.insert(
11171            language_server_name.into(),
11172            LspSettings {
11173                binary: None,
11174                settings: None,
11175                initialization_options: None,
11176            },
11177        );
11178    });
11179    cx.executor().run_until_parked();
11180    assert_eq!(
11181        server_restarts.load(atomic::Ordering::Acquire),
11182        2,
11183        "Should restart LSP server on another related LSP settings change"
11184    );
11185}
11186
11187#[gpui::test]
11188async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11189    init_test(cx, |_| {});
11190
11191    let mut cx = EditorLspTestContext::new_rust(
11192        lsp::ServerCapabilities {
11193            completion_provider: Some(lsp::CompletionOptions {
11194                trigger_characters: Some(vec![".".to_string()]),
11195                resolve_provider: Some(true),
11196                ..Default::default()
11197            }),
11198            ..Default::default()
11199        },
11200        cx,
11201    )
11202    .await;
11203
11204    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11205    cx.simulate_keystroke(".");
11206    let completion_item = lsp::CompletionItem {
11207        label: "some".into(),
11208        kind: Some(lsp::CompletionItemKind::SNIPPET),
11209        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11210        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11211            kind: lsp::MarkupKind::Markdown,
11212            value: "```rust\nSome(2)\n```".to_string(),
11213        })),
11214        deprecated: Some(false),
11215        sort_text: Some("fffffff2".to_string()),
11216        filter_text: Some("some".to_string()),
11217        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11218        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11219            range: lsp::Range {
11220                start: lsp::Position {
11221                    line: 0,
11222                    character: 22,
11223                },
11224                end: lsp::Position {
11225                    line: 0,
11226                    character: 22,
11227                },
11228            },
11229            new_text: "Some(2)".to_string(),
11230        })),
11231        additional_text_edits: Some(vec![lsp::TextEdit {
11232            range: lsp::Range {
11233                start: lsp::Position {
11234                    line: 0,
11235                    character: 20,
11236                },
11237                end: lsp::Position {
11238                    line: 0,
11239                    character: 22,
11240                },
11241            },
11242            new_text: "".to_string(),
11243        }]),
11244        ..Default::default()
11245    };
11246
11247    let closure_completion_item = completion_item.clone();
11248    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11249        let task_completion_item = closure_completion_item.clone();
11250        async move {
11251            Ok(Some(lsp::CompletionResponse::Array(vec![
11252                task_completion_item,
11253            ])))
11254        }
11255    });
11256
11257    request.next().await;
11258
11259    cx.condition(|editor, _| editor.context_menu_visible())
11260        .await;
11261    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11262        editor
11263            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11264            .unwrap()
11265    });
11266    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11267
11268    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11269        let task_completion_item = completion_item.clone();
11270        async move { Ok(task_completion_item) }
11271    })
11272    .next()
11273    .await
11274    .unwrap();
11275    apply_additional_edits.await.unwrap();
11276    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11277}
11278
11279#[gpui::test]
11280async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11281    cx: &mut gpui::TestAppContext,
11282) {
11283    init_test(cx, |_| {});
11284
11285    let mut cx = EditorLspTestContext::new_rust(
11286        lsp::ServerCapabilities {
11287            completion_provider: Some(lsp::CompletionOptions {
11288                trigger_characters: Some(vec![".".to_string()]),
11289                resolve_provider: Some(true),
11290                ..Default::default()
11291            }),
11292            ..Default::default()
11293        },
11294        cx,
11295    )
11296    .await;
11297
11298    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11299    cx.simulate_keystroke(".");
11300
11301    let item1 = lsp::CompletionItem {
11302        label: "method id()".to_string(),
11303        filter_text: Some("id".to_string()),
11304        detail: None,
11305        documentation: None,
11306        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11307            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11308            new_text: ".id".to_string(),
11309        })),
11310        ..lsp::CompletionItem::default()
11311    };
11312
11313    let item2 = lsp::CompletionItem {
11314        label: "other".to_string(),
11315        filter_text: Some("other".to_string()),
11316        detail: None,
11317        documentation: None,
11318        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11319            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11320            new_text: ".other".to_string(),
11321        })),
11322        ..lsp::CompletionItem::default()
11323    };
11324
11325    let item1 = item1.clone();
11326    cx.handle_request::<lsp::request::Completion, _, _>({
11327        let item1 = item1.clone();
11328        move |_, _, _| {
11329            let item1 = item1.clone();
11330            let item2 = item2.clone();
11331            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11332        }
11333    })
11334    .next()
11335    .await;
11336
11337    cx.condition(|editor, _| editor.context_menu_visible())
11338        .await;
11339    cx.update_editor(|editor, _, _| {
11340        let context_menu = editor.context_menu.borrow_mut();
11341        let context_menu = context_menu
11342            .as_ref()
11343            .expect("Should have the context menu deployed");
11344        match context_menu {
11345            CodeContextMenu::Completions(completions_menu) => {
11346                let completions = completions_menu.completions.borrow_mut();
11347                assert_eq!(
11348                    completions
11349                        .iter()
11350                        .map(|completion| &completion.label.text)
11351                        .collect::<Vec<_>>(),
11352                    vec!["method id()", "other"]
11353                )
11354            }
11355            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11356        }
11357    });
11358
11359    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11360        let item1 = item1.clone();
11361        move |_, item_to_resolve, _| {
11362            let item1 = item1.clone();
11363            async move {
11364                if item1 == item_to_resolve {
11365                    Ok(lsp::CompletionItem {
11366                        label: "method id()".to_string(),
11367                        filter_text: Some("id".to_string()),
11368                        detail: Some("Now resolved!".to_string()),
11369                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11370                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11371                            range: lsp::Range::new(
11372                                lsp::Position::new(0, 22),
11373                                lsp::Position::new(0, 22),
11374                            ),
11375                            new_text: ".id".to_string(),
11376                        })),
11377                        ..lsp::CompletionItem::default()
11378                    })
11379                } else {
11380                    Ok(item_to_resolve)
11381                }
11382            }
11383        }
11384    })
11385    .next()
11386    .await
11387    .unwrap();
11388    cx.run_until_parked();
11389
11390    cx.update_editor(|editor, window, cx| {
11391        editor.context_menu_next(&Default::default(), window, cx);
11392    });
11393
11394    cx.update_editor(|editor, _, _| {
11395        let context_menu = editor.context_menu.borrow_mut();
11396        let context_menu = context_menu
11397            .as_ref()
11398            .expect("Should have the context menu deployed");
11399        match context_menu {
11400            CodeContextMenu::Completions(completions_menu) => {
11401                let completions = completions_menu.completions.borrow_mut();
11402                assert_eq!(
11403                    completions
11404                        .iter()
11405                        .map(|completion| &completion.label.text)
11406                        .collect::<Vec<_>>(),
11407                    vec!["method id() Now resolved!", "other"],
11408                    "Should update first completion label, but not second as the filter text did not match."
11409                );
11410            }
11411            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11412        }
11413    });
11414}
11415
11416#[gpui::test]
11417async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11418    init_test(cx, |_| {});
11419
11420    let mut cx = EditorLspTestContext::new_rust(
11421        lsp::ServerCapabilities {
11422            completion_provider: Some(lsp::CompletionOptions {
11423                trigger_characters: Some(vec![".".to_string()]),
11424                resolve_provider: Some(true),
11425                ..Default::default()
11426            }),
11427            ..Default::default()
11428        },
11429        cx,
11430    )
11431    .await;
11432
11433    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11434    cx.simulate_keystroke(".");
11435
11436    let unresolved_item_1 = lsp::CompletionItem {
11437        label: "id".to_string(),
11438        filter_text: Some("id".to_string()),
11439        detail: None,
11440        documentation: None,
11441        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11442            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11443            new_text: ".id".to_string(),
11444        })),
11445        ..lsp::CompletionItem::default()
11446    };
11447    let resolved_item_1 = lsp::CompletionItem {
11448        additional_text_edits: Some(vec![lsp::TextEdit {
11449            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11450            new_text: "!!".to_string(),
11451        }]),
11452        ..unresolved_item_1.clone()
11453    };
11454    let unresolved_item_2 = lsp::CompletionItem {
11455        label: "other".to_string(),
11456        filter_text: Some("other".to_string()),
11457        detail: None,
11458        documentation: None,
11459        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11460            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11461            new_text: ".other".to_string(),
11462        })),
11463        ..lsp::CompletionItem::default()
11464    };
11465    let resolved_item_2 = lsp::CompletionItem {
11466        additional_text_edits: Some(vec![lsp::TextEdit {
11467            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11468            new_text: "??".to_string(),
11469        }]),
11470        ..unresolved_item_2.clone()
11471    };
11472
11473    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11474    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11475    cx.lsp
11476        .server
11477        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11478            let unresolved_item_1 = unresolved_item_1.clone();
11479            let resolved_item_1 = resolved_item_1.clone();
11480            let unresolved_item_2 = unresolved_item_2.clone();
11481            let resolved_item_2 = resolved_item_2.clone();
11482            let resolve_requests_1 = resolve_requests_1.clone();
11483            let resolve_requests_2 = resolve_requests_2.clone();
11484            move |unresolved_request, _| {
11485                let unresolved_item_1 = unresolved_item_1.clone();
11486                let resolved_item_1 = resolved_item_1.clone();
11487                let unresolved_item_2 = unresolved_item_2.clone();
11488                let resolved_item_2 = resolved_item_2.clone();
11489                let resolve_requests_1 = resolve_requests_1.clone();
11490                let resolve_requests_2 = resolve_requests_2.clone();
11491                async move {
11492                    if unresolved_request == unresolved_item_1 {
11493                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11494                        Ok(resolved_item_1.clone())
11495                    } else if unresolved_request == unresolved_item_2 {
11496                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11497                        Ok(resolved_item_2.clone())
11498                    } else {
11499                        panic!("Unexpected completion item {unresolved_request:?}")
11500                    }
11501                }
11502            }
11503        })
11504        .detach();
11505
11506    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11507        let unresolved_item_1 = unresolved_item_1.clone();
11508        let unresolved_item_2 = unresolved_item_2.clone();
11509        async move {
11510            Ok(Some(lsp::CompletionResponse::Array(vec![
11511                unresolved_item_1,
11512                unresolved_item_2,
11513            ])))
11514        }
11515    })
11516    .next()
11517    .await;
11518
11519    cx.condition(|editor, _| editor.context_menu_visible())
11520        .await;
11521    cx.update_editor(|editor, _, _| {
11522        let context_menu = editor.context_menu.borrow_mut();
11523        let context_menu = context_menu
11524            .as_ref()
11525            .expect("Should have the context menu deployed");
11526        match context_menu {
11527            CodeContextMenu::Completions(completions_menu) => {
11528                let completions = completions_menu.completions.borrow_mut();
11529                assert_eq!(
11530                    completions
11531                        .iter()
11532                        .map(|completion| &completion.label.text)
11533                        .collect::<Vec<_>>(),
11534                    vec!["id", "other"]
11535                )
11536            }
11537            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11538        }
11539    });
11540    cx.run_until_parked();
11541
11542    cx.update_editor(|editor, window, cx| {
11543        editor.context_menu_next(&ContextMenuNext, window, cx);
11544    });
11545    cx.run_until_parked();
11546    cx.update_editor(|editor, window, cx| {
11547        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11548    });
11549    cx.run_until_parked();
11550    cx.update_editor(|editor, window, cx| {
11551        editor.context_menu_next(&ContextMenuNext, window, cx);
11552    });
11553    cx.run_until_parked();
11554    cx.update_editor(|editor, window, cx| {
11555        editor
11556            .compose_completion(&ComposeCompletion::default(), window, cx)
11557            .expect("No task returned")
11558    })
11559    .await
11560    .expect("Completion failed");
11561    cx.run_until_parked();
11562
11563    cx.update_editor(|editor, _, cx| {
11564        assert_eq!(
11565            resolve_requests_1.load(atomic::Ordering::Acquire),
11566            1,
11567            "Should always resolve once despite multiple selections"
11568        );
11569        assert_eq!(
11570            resolve_requests_2.load(atomic::Ordering::Acquire),
11571            1,
11572            "Should always resolve once after multiple selections and applying the completion"
11573        );
11574        assert_eq!(
11575            editor.text(cx),
11576            "fn main() { let a = ??.other; }",
11577            "Should use resolved data when applying the completion"
11578        );
11579    });
11580}
11581
11582#[gpui::test]
11583async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11584    init_test(cx, |_| {});
11585
11586    let item_0 = lsp::CompletionItem {
11587        label: "abs".into(),
11588        insert_text: Some("abs".into()),
11589        data: Some(json!({ "very": "special"})),
11590        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11591        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11592            lsp::InsertReplaceEdit {
11593                new_text: "abs".to_string(),
11594                insert: lsp::Range::default(),
11595                replace: lsp::Range::default(),
11596            },
11597        )),
11598        ..lsp::CompletionItem::default()
11599    };
11600    let items = iter::once(item_0.clone())
11601        .chain((11..51).map(|i| lsp::CompletionItem {
11602            label: format!("item_{}", i),
11603            insert_text: Some(format!("item_{}", i)),
11604            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11605            ..lsp::CompletionItem::default()
11606        }))
11607        .collect::<Vec<_>>();
11608
11609    let default_commit_characters = vec!["?".to_string()];
11610    let default_data = json!({ "default": "data"});
11611    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11612    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11613    let default_edit_range = lsp::Range {
11614        start: lsp::Position {
11615            line: 0,
11616            character: 5,
11617        },
11618        end: lsp::Position {
11619            line: 0,
11620            character: 5,
11621        },
11622    };
11623
11624    let item_0_out = lsp::CompletionItem {
11625        commit_characters: Some(default_commit_characters.clone()),
11626        insert_text_format: Some(default_insert_text_format),
11627        ..item_0
11628    };
11629    let items_out = iter::once(item_0_out)
11630        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11631            commit_characters: Some(default_commit_characters.clone()),
11632            data: Some(default_data.clone()),
11633            insert_text_mode: Some(default_insert_text_mode),
11634            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11635                range: default_edit_range,
11636                new_text: item.label.clone(),
11637            })),
11638            ..item.clone()
11639        }))
11640        .collect::<Vec<lsp::CompletionItem>>();
11641
11642    let mut cx = EditorLspTestContext::new_rust(
11643        lsp::ServerCapabilities {
11644            completion_provider: Some(lsp::CompletionOptions {
11645                trigger_characters: Some(vec![".".to_string()]),
11646                resolve_provider: Some(true),
11647                ..Default::default()
11648            }),
11649            ..Default::default()
11650        },
11651        cx,
11652    )
11653    .await;
11654
11655    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11656    cx.simulate_keystroke(".");
11657
11658    let completion_data = default_data.clone();
11659    let completion_characters = default_commit_characters.clone();
11660    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11661        let default_data = completion_data.clone();
11662        let default_commit_characters = completion_characters.clone();
11663        let items = items.clone();
11664        async move {
11665            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11666                items,
11667                item_defaults: Some(lsp::CompletionListItemDefaults {
11668                    data: Some(default_data.clone()),
11669                    commit_characters: Some(default_commit_characters.clone()),
11670                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11671                        default_edit_range,
11672                    )),
11673                    insert_text_format: Some(default_insert_text_format),
11674                    insert_text_mode: Some(default_insert_text_mode),
11675                }),
11676                ..lsp::CompletionList::default()
11677            })))
11678        }
11679    })
11680    .next()
11681    .await;
11682
11683    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11684    cx.lsp
11685        .server
11686        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11687            let closure_resolved_items = resolved_items.clone();
11688            move |item_to_resolve, _| {
11689                let closure_resolved_items = closure_resolved_items.clone();
11690                async move {
11691                    closure_resolved_items.lock().push(item_to_resolve.clone());
11692                    Ok(item_to_resolve)
11693                }
11694            }
11695        })
11696        .detach();
11697
11698    cx.condition(|editor, _| editor.context_menu_visible())
11699        .await;
11700    cx.run_until_parked();
11701    cx.update_editor(|editor, _, _| {
11702        let menu = editor.context_menu.borrow_mut();
11703        match menu.as_ref().expect("should have the completions menu") {
11704            CodeContextMenu::Completions(completions_menu) => {
11705                assert_eq!(
11706                    completions_menu
11707                        .entries
11708                        .borrow()
11709                        .iter()
11710                        .map(|mat| mat.string.clone())
11711                        .collect::<Vec<String>>(),
11712                    items_out
11713                        .iter()
11714                        .map(|completion| completion.label.clone())
11715                        .collect::<Vec<String>>()
11716                );
11717            }
11718            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11719        }
11720    });
11721    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11722    // with 4 from the end.
11723    assert_eq!(
11724        *resolved_items.lock(),
11725        [
11726            &items_out[0..16],
11727            &items_out[items_out.len() - 4..items_out.len()]
11728        ]
11729        .concat()
11730        .iter()
11731        .cloned()
11732        .collect::<Vec<lsp::CompletionItem>>()
11733    );
11734    resolved_items.lock().clear();
11735
11736    cx.update_editor(|editor, window, cx| {
11737        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11738    });
11739    cx.run_until_parked();
11740    // Completions that have already been resolved are skipped.
11741    assert_eq!(
11742        *resolved_items.lock(),
11743        items_out[items_out.len() - 16..items_out.len() - 4]
11744            .iter()
11745            .cloned()
11746            .collect::<Vec<lsp::CompletionItem>>()
11747    );
11748    resolved_items.lock().clear();
11749}
11750
11751#[gpui::test]
11752async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11753    init_test(cx, |_| {});
11754
11755    let mut cx = EditorLspTestContext::new(
11756        Language::new(
11757            LanguageConfig {
11758                matcher: LanguageMatcher {
11759                    path_suffixes: vec!["jsx".into()],
11760                    ..Default::default()
11761                },
11762                overrides: [(
11763                    "element".into(),
11764                    LanguageConfigOverride {
11765                        word_characters: Override::Set(['-'].into_iter().collect()),
11766                        ..Default::default()
11767                    },
11768                )]
11769                .into_iter()
11770                .collect(),
11771                ..Default::default()
11772            },
11773            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11774        )
11775        .with_override_query("(jsx_self_closing_element) @element")
11776        .unwrap(),
11777        lsp::ServerCapabilities {
11778            completion_provider: Some(lsp::CompletionOptions {
11779                trigger_characters: Some(vec![":".to_string()]),
11780                ..Default::default()
11781            }),
11782            ..Default::default()
11783        },
11784        cx,
11785    )
11786    .await;
11787
11788    cx.lsp
11789        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11790            Ok(Some(lsp::CompletionResponse::Array(vec![
11791                lsp::CompletionItem {
11792                    label: "bg-blue".into(),
11793                    ..Default::default()
11794                },
11795                lsp::CompletionItem {
11796                    label: "bg-red".into(),
11797                    ..Default::default()
11798                },
11799                lsp::CompletionItem {
11800                    label: "bg-yellow".into(),
11801                    ..Default::default()
11802                },
11803            ])))
11804        });
11805
11806    cx.set_state(r#"<p class="bgˇ" />"#);
11807
11808    // Trigger completion when typing a dash, because the dash is an extra
11809    // word character in the 'element' scope, which contains the cursor.
11810    cx.simulate_keystroke("-");
11811    cx.executor().run_until_parked();
11812    cx.update_editor(|editor, _, _| {
11813        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11814        {
11815            assert_eq!(
11816                completion_menu_entries(&menu),
11817                &["bg-red", "bg-blue", "bg-yellow"]
11818            );
11819        } else {
11820            panic!("expected completion menu to be open");
11821        }
11822    });
11823
11824    cx.simulate_keystroke("l");
11825    cx.executor().run_until_parked();
11826    cx.update_editor(|editor, _, _| {
11827        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11828        {
11829            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11830        } else {
11831            panic!("expected completion menu to be open");
11832        }
11833    });
11834
11835    // When filtering completions, consider the character after the '-' to
11836    // be the start of a subword.
11837    cx.set_state(r#"<p class="yelˇ" />"#);
11838    cx.simulate_keystroke("l");
11839    cx.executor().run_until_parked();
11840    cx.update_editor(|editor, _, _| {
11841        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11842        {
11843            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11844        } else {
11845            panic!("expected completion menu to be open");
11846        }
11847    });
11848}
11849
11850fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11851    let entries = menu.entries.borrow();
11852    entries.iter().map(|mat| mat.string.clone()).collect()
11853}
11854
11855#[gpui::test]
11856async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11857    init_test(cx, |settings| {
11858        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11859            FormatterList(vec![Formatter::Prettier].into()),
11860        ))
11861    });
11862
11863    let fs = FakeFs::new(cx.executor());
11864    fs.insert_file("/file.ts", Default::default()).await;
11865
11866    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11867    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11868
11869    language_registry.add(Arc::new(Language::new(
11870        LanguageConfig {
11871            name: "TypeScript".into(),
11872            matcher: LanguageMatcher {
11873                path_suffixes: vec!["ts".to_string()],
11874                ..Default::default()
11875            },
11876            ..Default::default()
11877        },
11878        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11879    )));
11880    update_test_language_settings(cx, |settings| {
11881        settings.defaults.prettier = Some(PrettierSettings {
11882            allowed: true,
11883            ..PrettierSettings::default()
11884        });
11885    });
11886
11887    let test_plugin = "test_plugin";
11888    let _ = language_registry.register_fake_lsp(
11889        "TypeScript",
11890        FakeLspAdapter {
11891            prettier_plugins: vec![test_plugin],
11892            ..Default::default()
11893        },
11894    );
11895
11896    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11897    let buffer = project
11898        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11899        .await
11900        .unwrap();
11901
11902    let buffer_text = "one\ntwo\nthree\n";
11903    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11904    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11905    editor.update_in(cx, |editor, window, cx| {
11906        editor.set_text(buffer_text, window, cx)
11907    });
11908
11909    editor
11910        .update_in(cx, |editor, window, cx| {
11911            editor.perform_format(
11912                project.clone(),
11913                FormatTrigger::Manual,
11914                FormatTarget::Buffers,
11915                window,
11916                cx,
11917            )
11918        })
11919        .unwrap()
11920        .await;
11921    assert_eq!(
11922        editor.update(cx, |editor, cx| editor.text(cx)),
11923        buffer_text.to_string() + prettier_format_suffix,
11924        "Test prettier formatting was not applied to the original buffer text",
11925    );
11926
11927    update_test_language_settings(cx, |settings| {
11928        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11929    });
11930    let format = editor.update_in(cx, |editor, window, cx| {
11931        editor.perform_format(
11932            project.clone(),
11933            FormatTrigger::Manual,
11934            FormatTarget::Buffers,
11935            window,
11936            cx,
11937        )
11938    });
11939    format.await.unwrap();
11940    assert_eq!(
11941        editor.update(cx, |editor, cx| editor.text(cx)),
11942        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11943        "Autoformatting (via test prettier) was not applied to the original buffer text",
11944    );
11945}
11946
11947#[gpui::test]
11948async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11949    init_test(cx, |_| {});
11950    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11951    let base_text = indoc! {r#"
11952        struct Row;
11953        struct Row1;
11954        struct Row2;
11955
11956        struct Row4;
11957        struct Row5;
11958        struct Row6;
11959
11960        struct Row8;
11961        struct Row9;
11962        struct Row10;"#};
11963
11964    // When addition hunks are not adjacent to carets, no hunk revert is performed
11965    assert_hunk_revert(
11966        indoc! {r#"struct Row;
11967                   struct Row1;
11968                   struct Row1.1;
11969                   struct Row1.2;
11970                   struct Row2;ˇ
11971
11972                   struct Row4;
11973                   struct Row5;
11974                   struct Row6;
11975
11976                   struct Row8;
11977                   ˇstruct Row9;
11978                   struct Row9.1;
11979                   struct Row9.2;
11980                   struct Row9.3;
11981                   struct Row10;"#},
11982        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11983        indoc! {r#"struct Row;
11984                   struct Row1;
11985                   struct Row1.1;
11986                   struct Row1.2;
11987                   struct Row2;ˇ
11988
11989                   struct Row4;
11990                   struct Row5;
11991                   struct Row6;
11992
11993                   struct Row8;
11994                   ˇstruct Row9;
11995                   struct Row9.1;
11996                   struct Row9.2;
11997                   struct Row9.3;
11998                   struct Row10;"#},
11999        base_text,
12000        &mut cx,
12001    );
12002    // Same for selections
12003    assert_hunk_revert(
12004        indoc! {r#"struct Row;
12005                   struct Row1;
12006                   struct Row2;
12007                   struct Row2.1;
12008                   struct Row2.2;
12009                   «ˇ
12010                   struct Row4;
12011                   struct» Row5;
12012                   «struct Row6;
12013                   ˇ»
12014                   struct Row9.1;
12015                   struct Row9.2;
12016                   struct Row9.3;
12017                   struct Row8;
12018                   struct Row9;
12019                   struct Row10;"#},
12020        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12021        indoc! {r#"struct Row;
12022                   struct Row1;
12023                   struct Row2;
12024                   struct Row2.1;
12025                   struct Row2.2;
12026                   «ˇ
12027                   struct Row4;
12028                   struct» Row5;
12029                   «struct Row6;
12030                   ˇ»
12031                   struct Row9.1;
12032                   struct Row9.2;
12033                   struct Row9.3;
12034                   struct Row8;
12035                   struct Row9;
12036                   struct Row10;"#},
12037        base_text,
12038        &mut cx,
12039    );
12040
12041    // When carets and selections intersect the addition hunks, those are reverted.
12042    // Adjacent carets got merged.
12043    assert_hunk_revert(
12044        indoc! {r#"struct Row;
12045                   ˇ// something on the top
12046                   struct Row1;
12047                   struct Row2;
12048                   struct Roˇw3.1;
12049                   struct Row2.2;
12050                   struct Row2.3;ˇ
12051
12052                   struct Row4;
12053                   struct ˇRow5.1;
12054                   struct Row5.2;
12055                   struct «Rowˇ»5.3;
12056                   struct Row5;
12057                   struct Row6;
12058                   ˇ
12059                   struct Row9.1;
12060                   struct «Rowˇ»9.2;
12061                   struct «ˇRow»9.3;
12062                   struct Row8;
12063                   struct Row9;
12064                   «ˇ// something on bottom»
12065                   struct Row10;"#},
12066        vec![
12067            DiffHunkStatus::Added,
12068            DiffHunkStatus::Added,
12069            DiffHunkStatus::Added,
12070            DiffHunkStatus::Added,
12071            DiffHunkStatus::Added,
12072        ],
12073        indoc! {r#"struct Row;
12074                   ˇstruct Row1;
12075                   struct Row2;
12076                   ˇ
12077                   struct Row4;
12078                   ˇstruct Row5;
12079                   struct Row6;
12080                   ˇ
12081                   ˇstruct Row8;
12082                   struct Row9;
12083                   ˇstruct Row10;"#},
12084        base_text,
12085        &mut cx,
12086    );
12087}
12088
12089#[gpui::test]
12090async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12091    init_test(cx, |_| {});
12092    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12093    let base_text = indoc! {r#"
12094        struct Row;
12095        struct Row1;
12096        struct Row2;
12097
12098        struct Row4;
12099        struct Row5;
12100        struct Row6;
12101
12102        struct Row8;
12103        struct Row9;
12104        struct Row10;"#};
12105
12106    // Modification hunks behave the same as the addition ones.
12107    assert_hunk_revert(
12108        indoc! {r#"struct Row;
12109                   struct Row1;
12110                   struct Row33;
12111                   ˇ
12112                   struct Row4;
12113                   struct Row5;
12114                   struct Row6;
12115                   ˇ
12116                   struct Row99;
12117                   struct Row9;
12118                   struct Row10;"#},
12119        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12120        indoc! {r#"struct Row;
12121                   struct Row1;
12122                   struct Row33;
12123                   ˇ
12124                   struct Row4;
12125                   struct Row5;
12126                   struct Row6;
12127                   ˇ
12128                   struct Row99;
12129                   struct Row9;
12130                   struct Row10;"#},
12131        base_text,
12132        &mut cx,
12133    );
12134    assert_hunk_revert(
12135        indoc! {r#"struct Row;
12136                   struct Row1;
12137                   struct Row33;
12138                   «ˇ
12139                   struct Row4;
12140                   struct» Row5;
12141                   «struct Row6;
12142                   ˇ»
12143                   struct Row99;
12144                   struct Row9;
12145                   struct Row10;"#},
12146        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12147        indoc! {r#"struct Row;
12148                   struct Row1;
12149                   struct Row33;
12150                   «ˇ
12151                   struct Row4;
12152                   struct» Row5;
12153                   «struct Row6;
12154                   ˇ»
12155                   struct Row99;
12156                   struct Row9;
12157                   struct Row10;"#},
12158        base_text,
12159        &mut cx,
12160    );
12161
12162    assert_hunk_revert(
12163        indoc! {r#"ˇstruct Row1.1;
12164                   struct Row1;
12165                   «ˇstr»uct Row22;
12166
12167                   struct ˇRow44;
12168                   struct Row5;
12169                   struct «Rˇ»ow66;ˇ
12170
12171                   «struˇ»ct Row88;
12172                   struct Row9;
12173                   struct Row1011;ˇ"#},
12174        vec![
12175            DiffHunkStatus::Modified,
12176            DiffHunkStatus::Modified,
12177            DiffHunkStatus::Modified,
12178            DiffHunkStatus::Modified,
12179            DiffHunkStatus::Modified,
12180            DiffHunkStatus::Modified,
12181        ],
12182        indoc! {r#"struct Row;
12183                   ˇstruct Row1;
12184                   struct Row2;
12185                   ˇ
12186                   struct Row4;
12187                   ˇstruct Row5;
12188                   struct Row6;
12189                   ˇ
12190                   struct Row8;
12191                   ˇstruct Row9;
12192                   struct Row10;ˇ"#},
12193        base_text,
12194        &mut cx,
12195    );
12196}
12197
12198#[gpui::test]
12199async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12200    init_test(cx, |_| {});
12201    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12202    let base_text = indoc! {r#"
12203        one
12204
12205        two
12206        three
12207        "#};
12208
12209    cx.set_diff_base(base_text);
12210    cx.set_state("\nˇ\n");
12211    cx.executor().run_until_parked();
12212    cx.update_editor(|editor, _window, cx| {
12213        editor.expand_selected_diff_hunks(cx);
12214    });
12215    cx.executor().run_until_parked();
12216    cx.update_editor(|editor, window, cx| {
12217        editor.backspace(&Default::default(), window, cx);
12218    });
12219    cx.run_until_parked();
12220    cx.assert_state_with_diff(
12221        indoc! {r#"
12222
12223        - two
12224        - threeˇ
12225        +
12226        "#}
12227        .to_string(),
12228    );
12229}
12230
12231#[gpui::test]
12232async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12233    init_test(cx, |_| {});
12234    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12235    let base_text = indoc! {r#"struct Row;
12236struct Row1;
12237struct Row2;
12238
12239struct Row4;
12240struct Row5;
12241struct Row6;
12242
12243struct Row8;
12244struct Row9;
12245struct Row10;"#};
12246
12247    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12248    assert_hunk_revert(
12249        indoc! {r#"struct Row;
12250                   struct Row2;
12251
12252                   ˇstruct Row4;
12253                   struct Row5;
12254                   struct Row6;
12255                   ˇ
12256                   struct Row8;
12257                   struct Row10;"#},
12258        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12259        indoc! {r#"struct Row;
12260                   struct Row2;
12261
12262                   ˇstruct Row4;
12263                   struct Row5;
12264                   struct Row6;
12265                   ˇ
12266                   struct Row8;
12267                   struct Row10;"#},
12268        base_text,
12269        &mut cx,
12270    );
12271    assert_hunk_revert(
12272        indoc! {r#"struct Row;
12273                   struct Row2;
12274
12275                   «ˇstruct Row4;
12276                   struct» Row5;
12277                   «struct Row6;
12278                   ˇ»
12279                   struct Row8;
12280                   struct Row10;"#},
12281        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12282        indoc! {r#"struct Row;
12283                   struct Row2;
12284
12285                   «ˇstruct Row4;
12286                   struct» Row5;
12287                   «struct Row6;
12288                   ˇ»
12289                   struct Row8;
12290                   struct Row10;"#},
12291        base_text,
12292        &mut cx,
12293    );
12294
12295    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12296    assert_hunk_revert(
12297        indoc! {r#"struct Row;
12298                   ˇstruct Row2;
12299
12300                   struct Row4;
12301                   struct Row5;
12302                   struct Row6;
12303
12304                   struct Row8;ˇ
12305                   struct Row10;"#},
12306        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12307        indoc! {r#"struct Row;
12308                   struct Row1;
12309                   ˇstruct Row2;
12310
12311                   struct Row4;
12312                   struct Row5;
12313                   struct Row6;
12314
12315                   struct Row8;ˇ
12316                   struct Row9;
12317                   struct Row10;"#},
12318        base_text,
12319        &mut cx,
12320    );
12321    assert_hunk_revert(
12322        indoc! {r#"struct Row;
12323                   struct Row2«ˇ;
12324                   struct Row4;
12325                   struct» Row5;
12326                   «struct Row6;
12327
12328                   struct Row8;ˇ»
12329                   struct Row10;"#},
12330        vec![
12331            DiffHunkStatus::Removed,
12332            DiffHunkStatus::Removed,
12333            DiffHunkStatus::Removed,
12334        ],
12335        indoc! {r#"struct Row;
12336                   struct Row1;
12337                   struct Row2«ˇ;
12338
12339                   struct Row4;
12340                   struct» Row5;
12341                   «struct Row6;
12342
12343                   struct Row8;ˇ»
12344                   struct Row9;
12345                   struct Row10;"#},
12346        base_text,
12347        &mut cx,
12348    );
12349}
12350
12351#[gpui::test]
12352async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12353    init_test(cx, |_| {});
12354
12355    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12356    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12357    let base_text_3 =
12358        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12359
12360    let text_1 = edit_first_char_of_every_line(base_text_1);
12361    let text_2 = edit_first_char_of_every_line(base_text_2);
12362    let text_3 = edit_first_char_of_every_line(base_text_3);
12363
12364    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12365    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12366    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12367
12368    let multibuffer = cx.new(|cx| {
12369        let mut multibuffer = MultiBuffer::new(ReadWrite);
12370        multibuffer.push_excerpts(
12371            buffer_1.clone(),
12372            [
12373                ExcerptRange {
12374                    context: Point::new(0, 0)..Point::new(3, 0),
12375                    primary: None,
12376                },
12377                ExcerptRange {
12378                    context: Point::new(5, 0)..Point::new(7, 0),
12379                    primary: None,
12380                },
12381                ExcerptRange {
12382                    context: Point::new(9, 0)..Point::new(10, 4),
12383                    primary: None,
12384                },
12385            ],
12386            cx,
12387        );
12388        multibuffer.push_excerpts(
12389            buffer_2.clone(),
12390            [
12391                ExcerptRange {
12392                    context: Point::new(0, 0)..Point::new(3, 0),
12393                    primary: None,
12394                },
12395                ExcerptRange {
12396                    context: Point::new(5, 0)..Point::new(7, 0),
12397                    primary: None,
12398                },
12399                ExcerptRange {
12400                    context: Point::new(9, 0)..Point::new(10, 4),
12401                    primary: None,
12402                },
12403            ],
12404            cx,
12405        );
12406        multibuffer.push_excerpts(
12407            buffer_3.clone(),
12408            [
12409                ExcerptRange {
12410                    context: Point::new(0, 0)..Point::new(3, 0),
12411                    primary: None,
12412                },
12413                ExcerptRange {
12414                    context: Point::new(5, 0)..Point::new(7, 0),
12415                    primary: None,
12416                },
12417                ExcerptRange {
12418                    context: Point::new(9, 0)..Point::new(10, 4),
12419                    primary: None,
12420                },
12421            ],
12422            cx,
12423        );
12424        multibuffer
12425    });
12426
12427    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12428    editor.update_in(cx, |editor, _window, cx| {
12429        for (buffer, diff_base) in [
12430            (buffer_1.clone(), base_text_1),
12431            (buffer_2.clone(), base_text_2),
12432            (buffer_3.clone(), base_text_3),
12433        ] {
12434            let change_set =
12435                cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
12436            editor
12437                .buffer
12438                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12439        }
12440    });
12441    cx.executor().run_until_parked();
12442
12443    editor.update_in(cx, |editor, window, cx| {
12444        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}");
12445        editor.select_all(&SelectAll, window, cx);
12446        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12447    });
12448    cx.executor().run_until_parked();
12449
12450    // When all ranges are selected, all buffer hunks are reverted.
12451    editor.update(cx, |editor, cx| {
12452        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");
12453    });
12454    buffer_1.update(cx, |buffer, _| {
12455        assert_eq!(buffer.text(), base_text_1);
12456    });
12457    buffer_2.update(cx, |buffer, _| {
12458        assert_eq!(buffer.text(), base_text_2);
12459    });
12460    buffer_3.update(cx, |buffer, _| {
12461        assert_eq!(buffer.text(), base_text_3);
12462    });
12463
12464    editor.update_in(cx, |editor, window, cx| {
12465        editor.undo(&Default::default(), window, cx);
12466    });
12467
12468    editor.update_in(cx, |editor, window, cx| {
12469        editor.change_selections(None, window, cx, |s| {
12470            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12471        });
12472        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12473    });
12474
12475    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12476    // but not affect buffer_2 and its related excerpts.
12477    editor.update(cx, |editor, cx| {
12478        assert_eq!(
12479            editor.text(cx),
12480            "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}"
12481        );
12482    });
12483    buffer_1.update(cx, |buffer, _| {
12484        assert_eq!(buffer.text(), base_text_1);
12485    });
12486    buffer_2.update(cx, |buffer, _| {
12487        assert_eq!(
12488            buffer.text(),
12489            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12490        );
12491    });
12492    buffer_3.update(cx, |buffer, _| {
12493        assert_eq!(
12494            buffer.text(),
12495            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12496        );
12497    });
12498
12499    fn edit_first_char_of_every_line(text: &str) -> String {
12500        text.split('\n')
12501            .map(|line| format!("X{}", &line[1..]))
12502            .collect::<Vec<_>>()
12503            .join("\n")
12504    }
12505}
12506
12507#[gpui::test]
12508async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12509    init_test(cx, |_| {});
12510
12511    let cols = 4;
12512    let rows = 10;
12513    let sample_text_1 = sample_text(rows, cols, 'a');
12514    assert_eq!(
12515        sample_text_1,
12516        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12517    );
12518    let sample_text_2 = sample_text(rows, cols, 'l');
12519    assert_eq!(
12520        sample_text_2,
12521        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12522    );
12523    let sample_text_3 = sample_text(rows, cols, 'v');
12524    assert_eq!(
12525        sample_text_3,
12526        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12527    );
12528
12529    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12530    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12531    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12532
12533    let multi_buffer = cx.new(|cx| {
12534        let mut multibuffer = MultiBuffer::new(ReadWrite);
12535        multibuffer.push_excerpts(
12536            buffer_1.clone(),
12537            [
12538                ExcerptRange {
12539                    context: Point::new(0, 0)..Point::new(3, 0),
12540                    primary: None,
12541                },
12542                ExcerptRange {
12543                    context: Point::new(5, 0)..Point::new(7, 0),
12544                    primary: None,
12545                },
12546                ExcerptRange {
12547                    context: Point::new(9, 0)..Point::new(10, 4),
12548                    primary: None,
12549                },
12550            ],
12551            cx,
12552        );
12553        multibuffer.push_excerpts(
12554            buffer_2.clone(),
12555            [
12556                ExcerptRange {
12557                    context: Point::new(0, 0)..Point::new(3, 0),
12558                    primary: None,
12559                },
12560                ExcerptRange {
12561                    context: Point::new(5, 0)..Point::new(7, 0),
12562                    primary: None,
12563                },
12564                ExcerptRange {
12565                    context: Point::new(9, 0)..Point::new(10, 4),
12566                    primary: None,
12567                },
12568            ],
12569            cx,
12570        );
12571        multibuffer.push_excerpts(
12572            buffer_3.clone(),
12573            [
12574                ExcerptRange {
12575                    context: Point::new(0, 0)..Point::new(3, 0),
12576                    primary: None,
12577                },
12578                ExcerptRange {
12579                    context: Point::new(5, 0)..Point::new(7, 0),
12580                    primary: None,
12581                },
12582                ExcerptRange {
12583                    context: Point::new(9, 0)..Point::new(10, 4),
12584                    primary: None,
12585                },
12586            ],
12587            cx,
12588        );
12589        multibuffer
12590    });
12591
12592    let fs = FakeFs::new(cx.executor());
12593    fs.insert_tree(
12594        "/a",
12595        json!({
12596            "main.rs": sample_text_1,
12597            "other.rs": sample_text_2,
12598            "lib.rs": sample_text_3,
12599        }),
12600    )
12601    .await;
12602    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12603    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12604    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12605    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12606        Editor::new(
12607            EditorMode::Full,
12608            multi_buffer,
12609            Some(project.clone()),
12610            true,
12611            window,
12612            cx,
12613        )
12614    });
12615    let multibuffer_item_id = workspace
12616        .update(cx, |workspace, window, cx| {
12617            assert!(
12618                workspace.active_item(cx).is_none(),
12619                "active item should be None before the first item is added"
12620            );
12621            workspace.add_item_to_active_pane(
12622                Box::new(multi_buffer_editor.clone()),
12623                None,
12624                true,
12625                window,
12626                cx,
12627            );
12628            let active_item = workspace
12629                .active_item(cx)
12630                .expect("should have an active item after adding the multi buffer");
12631            assert!(
12632                !active_item.is_singleton(cx),
12633                "A multi buffer was expected to active after adding"
12634            );
12635            active_item.item_id()
12636        })
12637        .unwrap();
12638    cx.executor().run_until_parked();
12639
12640    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12641        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12642            s.select_ranges(Some(1..2))
12643        });
12644        editor.open_excerpts(&OpenExcerpts, window, cx);
12645    });
12646    cx.executor().run_until_parked();
12647    let first_item_id = workspace
12648        .update(cx, |workspace, window, cx| {
12649            let active_item = workspace
12650                .active_item(cx)
12651                .expect("should have an active item after navigating into the 1st buffer");
12652            let first_item_id = active_item.item_id();
12653            assert_ne!(
12654                first_item_id, multibuffer_item_id,
12655                "Should navigate into the 1st buffer and activate it"
12656            );
12657            assert!(
12658                active_item.is_singleton(cx),
12659                "New active item should be a singleton buffer"
12660            );
12661            assert_eq!(
12662                active_item
12663                    .act_as::<Editor>(cx)
12664                    .expect("should have navigated into an editor for the 1st buffer")
12665                    .read(cx)
12666                    .text(cx),
12667                sample_text_1
12668            );
12669
12670            workspace
12671                .go_back(workspace.active_pane().downgrade(), window, cx)
12672                .detach_and_log_err(cx);
12673
12674            first_item_id
12675        })
12676        .unwrap();
12677    cx.executor().run_until_parked();
12678    workspace
12679        .update(cx, |workspace, _, cx| {
12680            let active_item = workspace
12681                .active_item(cx)
12682                .expect("should have an active item after navigating back");
12683            assert_eq!(
12684                active_item.item_id(),
12685                multibuffer_item_id,
12686                "Should navigate back to the multi buffer"
12687            );
12688            assert!(!active_item.is_singleton(cx));
12689        })
12690        .unwrap();
12691
12692    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12693        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12694            s.select_ranges(Some(39..40))
12695        });
12696        editor.open_excerpts(&OpenExcerpts, window, cx);
12697    });
12698    cx.executor().run_until_parked();
12699    let second_item_id = workspace
12700        .update(cx, |workspace, window, cx| {
12701            let active_item = workspace
12702                .active_item(cx)
12703                .expect("should have an active item after navigating into the 2nd buffer");
12704            let second_item_id = active_item.item_id();
12705            assert_ne!(
12706                second_item_id, multibuffer_item_id,
12707                "Should navigate away from the multibuffer"
12708            );
12709            assert_ne!(
12710                second_item_id, first_item_id,
12711                "Should navigate into the 2nd buffer and activate it"
12712            );
12713            assert!(
12714                active_item.is_singleton(cx),
12715                "New active item should be a singleton buffer"
12716            );
12717            assert_eq!(
12718                active_item
12719                    .act_as::<Editor>(cx)
12720                    .expect("should have navigated into an editor")
12721                    .read(cx)
12722                    .text(cx),
12723                sample_text_2
12724            );
12725
12726            workspace
12727                .go_back(workspace.active_pane().downgrade(), window, cx)
12728                .detach_and_log_err(cx);
12729
12730            second_item_id
12731        })
12732        .unwrap();
12733    cx.executor().run_until_parked();
12734    workspace
12735        .update(cx, |workspace, _, cx| {
12736            let active_item = workspace
12737                .active_item(cx)
12738                .expect("should have an active item after navigating back from the 2nd buffer");
12739            assert_eq!(
12740                active_item.item_id(),
12741                multibuffer_item_id,
12742                "Should navigate back from the 2nd buffer to the multi buffer"
12743            );
12744            assert!(!active_item.is_singleton(cx));
12745        })
12746        .unwrap();
12747
12748    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12749        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12750            s.select_ranges(Some(70..70))
12751        });
12752        editor.open_excerpts(&OpenExcerpts, window, cx);
12753    });
12754    cx.executor().run_until_parked();
12755    workspace
12756        .update(cx, |workspace, window, cx| {
12757            let active_item = workspace
12758                .active_item(cx)
12759                .expect("should have an active item after navigating into the 3rd buffer");
12760            let third_item_id = active_item.item_id();
12761            assert_ne!(
12762                third_item_id, multibuffer_item_id,
12763                "Should navigate into the 3rd buffer and activate it"
12764            );
12765            assert_ne!(third_item_id, first_item_id);
12766            assert_ne!(third_item_id, second_item_id);
12767            assert!(
12768                active_item.is_singleton(cx),
12769                "New active item should be a singleton buffer"
12770            );
12771            assert_eq!(
12772                active_item
12773                    .act_as::<Editor>(cx)
12774                    .expect("should have navigated into an editor")
12775                    .read(cx)
12776                    .text(cx),
12777                sample_text_3
12778            );
12779
12780            workspace
12781                .go_back(workspace.active_pane().downgrade(), window, cx)
12782                .detach_and_log_err(cx);
12783        })
12784        .unwrap();
12785    cx.executor().run_until_parked();
12786    workspace
12787        .update(cx, |workspace, _, cx| {
12788            let active_item = workspace
12789                .active_item(cx)
12790                .expect("should have an active item after navigating back from the 3rd buffer");
12791            assert_eq!(
12792                active_item.item_id(),
12793                multibuffer_item_id,
12794                "Should navigate back from the 3rd buffer to the multi buffer"
12795            );
12796            assert!(!active_item.is_singleton(cx));
12797        })
12798        .unwrap();
12799}
12800
12801#[gpui::test]
12802async fn test_toggle_selected_diff_hunks(
12803    executor: BackgroundExecutor,
12804    cx: &mut gpui::TestAppContext,
12805) {
12806    init_test(cx, |_| {});
12807
12808    let mut cx = EditorTestContext::new(cx).await;
12809
12810    let diff_base = r#"
12811        use some::mod;
12812
12813        const A: u32 = 42;
12814
12815        fn main() {
12816            println!("hello");
12817
12818            println!("world");
12819        }
12820        "#
12821    .unindent();
12822
12823    cx.set_state(
12824        &r#"
12825        use some::modified;
12826
12827        ˇ
12828        fn main() {
12829            println!("hello there");
12830
12831            println!("around the");
12832            println!("world");
12833        }
12834        "#
12835        .unindent(),
12836    );
12837
12838    cx.set_diff_base(&diff_base);
12839    executor.run_until_parked();
12840
12841    cx.update_editor(|editor, window, cx| {
12842        editor.go_to_next_hunk(&GoToHunk, window, cx);
12843        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12844    });
12845    executor.run_until_parked();
12846    cx.assert_state_with_diff(
12847        r#"
12848          use some::modified;
12849
12850
12851          fn main() {
12852        -     println!("hello");
12853        + ˇ    println!("hello there");
12854
12855              println!("around the");
12856              println!("world");
12857          }
12858        "#
12859        .unindent(),
12860    );
12861
12862    cx.update_editor(|editor, window, cx| {
12863        for _ in 0..2 {
12864            editor.go_to_next_hunk(&GoToHunk, window, cx);
12865            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12866        }
12867    });
12868    executor.run_until_parked();
12869    cx.assert_state_with_diff(
12870        r#"
12871        - use some::mod;
12872        + ˇuse some::modified;
12873
12874
12875          fn main() {
12876        -     println!("hello");
12877        +     println!("hello there");
12878
12879        +     println!("around the");
12880              println!("world");
12881          }
12882        "#
12883        .unindent(),
12884    );
12885
12886    cx.update_editor(|editor, window, cx| {
12887        editor.go_to_next_hunk(&GoToHunk, window, cx);
12888        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12889    });
12890    executor.run_until_parked();
12891    cx.assert_state_with_diff(
12892        r#"
12893        - use some::mod;
12894        + use some::modified;
12895
12896        - const A: u32 = 42;
12897          ˇ
12898          fn main() {
12899        -     println!("hello");
12900        +     println!("hello there");
12901
12902        +     println!("around the");
12903              println!("world");
12904          }
12905        "#
12906        .unindent(),
12907    );
12908
12909    cx.update_editor(|editor, window, cx| {
12910        editor.cancel(&Cancel, window, cx);
12911    });
12912
12913    cx.assert_state_with_diff(
12914        r#"
12915          use some::modified;
12916
12917          ˇ
12918          fn main() {
12919              println!("hello there");
12920
12921              println!("around the");
12922              println!("world");
12923          }
12924        "#
12925        .unindent(),
12926    );
12927}
12928
12929#[gpui::test]
12930async fn test_diff_base_change_with_expanded_diff_hunks(
12931    executor: BackgroundExecutor,
12932    cx: &mut gpui::TestAppContext,
12933) {
12934    init_test(cx, |_| {});
12935
12936    let mut cx = EditorTestContext::new(cx).await;
12937
12938    let diff_base = r#"
12939        use some::mod1;
12940        use some::mod2;
12941
12942        const A: u32 = 42;
12943        const B: u32 = 42;
12944        const C: u32 = 42;
12945
12946        fn main() {
12947            println!("hello");
12948
12949            println!("world");
12950        }
12951        "#
12952    .unindent();
12953
12954    cx.set_state(
12955        &r#"
12956        use some::mod2;
12957
12958        const A: u32 = 42;
12959        const C: u32 = 42;
12960
12961        fn main(ˇ) {
12962            //println!("hello");
12963
12964            println!("world");
12965            //
12966            //
12967        }
12968        "#
12969        .unindent(),
12970    );
12971
12972    cx.set_diff_base(&diff_base);
12973    executor.run_until_parked();
12974
12975    cx.update_editor(|editor, window, cx| {
12976        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
12977    });
12978    executor.run_until_parked();
12979    cx.assert_state_with_diff(
12980        r#"
12981        - use some::mod1;
12982          use some::mod2;
12983
12984          const A: u32 = 42;
12985        - const B: u32 = 42;
12986          const C: u32 = 42;
12987
12988          fn main(ˇ) {
12989        -     println!("hello");
12990        +     //println!("hello");
12991
12992              println!("world");
12993        +     //
12994        +     //
12995          }
12996        "#
12997        .unindent(),
12998    );
12999
13000    cx.set_diff_base("new diff base!");
13001    executor.run_until_parked();
13002    cx.assert_state_with_diff(
13003        r#"
13004          use some::mod2;
13005
13006          const A: u32 = 42;
13007          const C: u32 = 42;
13008
13009          fn main(ˇ) {
13010              //println!("hello");
13011
13012              println!("world");
13013              //
13014              //
13015          }
13016        "#
13017        .unindent(),
13018    );
13019
13020    cx.update_editor(|editor, window, cx| {
13021        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13022    });
13023    executor.run_until_parked();
13024    cx.assert_state_with_diff(
13025        r#"
13026        - new diff base!
13027        + use some::mod2;
13028        +
13029        + const A: u32 = 42;
13030        + const C: u32 = 42;
13031        +
13032        + fn main(ˇ) {
13033        +     //println!("hello");
13034        +
13035        +     println!("world");
13036        +     //
13037        +     //
13038        + }
13039        "#
13040        .unindent(),
13041    );
13042}
13043
13044#[gpui::test]
13045async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13046    init_test(cx, |_| {});
13047
13048    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13049    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13050    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13051    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13052    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13053    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13054
13055    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13056    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13057    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13058
13059    let multi_buffer = cx.new(|cx| {
13060        let mut multibuffer = MultiBuffer::new(ReadWrite);
13061        multibuffer.push_excerpts(
13062            buffer_1.clone(),
13063            [
13064                ExcerptRange {
13065                    context: Point::new(0, 0)..Point::new(3, 0),
13066                    primary: None,
13067                },
13068                ExcerptRange {
13069                    context: Point::new(5, 0)..Point::new(7, 0),
13070                    primary: None,
13071                },
13072                ExcerptRange {
13073                    context: Point::new(9, 0)..Point::new(10, 3),
13074                    primary: None,
13075                },
13076            ],
13077            cx,
13078        );
13079        multibuffer.push_excerpts(
13080            buffer_2.clone(),
13081            [
13082                ExcerptRange {
13083                    context: Point::new(0, 0)..Point::new(3, 0),
13084                    primary: None,
13085                },
13086                ExcerptRange {
13087                    context: Point::new(5, 0)..Point::new(7, 0),
13088                    primary: None,
13089                },
13090                ExcerptRange {
13091                    context: Point::new(9, 0)..Point::new(10, 3),
13092                    primary: None,
13093                },
13094            ],
13095            cx,
13096        );
13097        multibuffer.push_excerpts(
13098            buffer_3.clone(),
13099            [
13100                ExcerptRange {
13101                    context: Point::new(0, 0)..Point::new(3, 0),
13102                    primary: None,
13103                },
13104                ExcerptRange {
13105                    context: Point::new(5, 0)..Point::new(7, 0),
13106                    primary: None,
13107                },
13108                ExcerptRange {
13109                    context: Point::new(9, 0)..Point::new(10, 3),
13110                    primary: None,
13111                },
13112            ],
13113            cx,
13114        );
13115        multibuffer
13116    });
13117
13118    let editor = cx.add_window(|window, cx| {
13119        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13120    });
13121    editor
13122        .update(cx, |editor, _window, cx| {
13123            for (buffer, diff_base) in [
13124                (buffer_1.clone(), file_1_old),
13125                (buffer_2.clone(), file_2_old),
13126                (buffer_3.clone(), file_3_old),
13127            ] {
13128                let change_set =
13129                    cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
13130                editor
13131                    .buffer
13132                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13133            }
13134        })
13135        .unwrap();
13136
13137    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13138    cx.run_until_parked();
13139
13140    cx.assert_editor_state(
13141        &"
13142            ˇaaa
13143            ccc
13144            ddd
13145
13146            ggg
13147            hhh
13148
13149
13150            lll
13151            mmm
13152            NNN
13153
13154            qqq
13155            rrr
13156
13157            uuu
13158            111
13159            222
13160            333
13161
13162            666
13163            777
13164
13165            000
13166            !!!"
13167        .unindent(),
13168    );
13169
13170    cx.update_editor(|editor, window, cx| {
13171        editor.select_all(&SelectAll, window, cx);
13172        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13173    });
13174    cx.executor().run_until_parked();
13175
13176    cx.assert_state_with_diff(
13177        "
13178            «aaa
13179          - bbb
13180            ccc
13181            ddd
13182
13183            ggg
13184            hhh
13185
13186
13187            lll
13188            mmm
13189          - nnn
13190          + NNN
13191
13192            qqq
13193            rrr
13194
13195            uuu
13196            111
13197            222
13198            333
13199
13200          + 666
13201            777
13202
13203            000
13204            !!!ˇ»"
13205            .unindent(),
13206    );
13207}
13208
13209#[gpui::test]
13210async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13211    init_test(cx, |_| {});
13212
13213    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13214    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13215
13216    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13217    let multi_buffer = cx.new(|cx| {
13218        let mut multibuffer = MultiBuffer::new(ReadWrite);
13219        multibuffer.push_excerpts(
13220            buffer.clone(),
13221            [
13222                ExcerptRange {
13223                    context: Point::new(0, 0)..Point::new(2, 0),
13224                    primary: None,
13225                },
13226                ExcerptRange {
13227                    context: Point::new(4, 0)..Point::new(7, 0),
13228                    primary: None,
13229                },
13230                ExcerptRange {
13231                    context: Point::new(9, 0)..Point::new(10, 0),
13232                    primary: None,
13233                },
13234            ],
13235            cx,
13236        );
13237        multibuffer
13238    });
13239
13240    let editor = cx.add_window(|window, cx| {
13241        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13242    });
13243    editor
13244        .update(cx, |editor, _window, cx| {
13245            let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base, &buffer, cx));
13246            editor
13247                .buffer
13248                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13249        })
13250        .unwrap();
13251
13252    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13253    cx.run_until_parked();
13254
13255    cx.update_editor(|editor, window, cx| {
13256        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13257    });
13258    cx.executor().run_until_parked();
13259
13260    // When the start of a hunk coincides with the start of its excerpt,
13261    // the hunk is expanded. When the start of a a hunk is earlier than
13262    // the start of its excerpt, the hunk is not expanded.
13263    cx.assert_state_with_diff(
13264        "
13265            ˇaaa
13266          - bbb
13267          + BBB
13268
13269          - ddd
13270          - eee
13271          + DDD
13272          + EEE
13273            fff
13274
13275            iii
13276        "
13277        .unindent(),
13278    );
13279}
13280
13281#[gpui::test]
13282async fn test_edits_around_expanded_insertion_hunks(
13283    executor: BackgroundExecutor,
13284    cx: &mut gpui::TestAppContext,
13285) {
13286    init_test(cx, |_| {});
13287
13288    let mut cx = EditorTestContext::new(cx).await;
13289
13290    let diff_base = r#"
13291        use some::mod1;
13292        use some::mod2;
13293
13294        const A: u32 = 42;
13295
13296        fn main() {
13297            println!("hello");
13298
13299            println!("world");
13300        }
13301        "#
13302    .unindent();
13303    executor.run_until_parked();
13304    cx.set_state(
13305        &r#"
13306        use some::mod1;
13307        use some::mod2;
13308
13309        const A: u32 = 42;
13310        const B: u32 = 42;
13311        const C: u32 = 42;
13312        ˇ
13313
13314        fn main() {
13315            println!("hello");
13316
13317            println!("world");
13318        }
13319        "#
13320        .unindent(),
13321    );
13322
13323    cx.set_diff_base(&diff_base);
13324    executor.run_until_parked();
13325
13326    cx.update_editor(|editor, window, cx| {
13327        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13328    });
13329    executor.run_until_parked();
13330
13331    cx.assert_state_with_diff(
13332        r#"
13333        use some::mod1;
13334        use some::mod2;
13335
13336        const A: u32 = 42;
13337      + const B: u32 = 42;
13338      + const C: u32 = 42;
13339      + ˇ
13340
13341        fn main() {
13342            println!("hello");
13343
13344            println!("world");
13345        }
13346      "#
13347        .unindent(),
13348    );
13349
13350    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13351    executor.run_until_parked();
13352
13353    cx.assert_state_with_diff(
13354        r#"
13355        use some::mod1;
13356        use some::mod2;
13357
13358        const A: u32 = 42;
13359      + const B: u32 = 42;
13360      + const C: u32 = 42;
13361      + const D: u32 = 42;
13362      + ˇ
13363
13364        fn main() {
13365            println!("hello");
13366
13367            println!("world");
13368        }
13369      "#
13370        .unindent(),
13371    );
13372
13373    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13374    executor.run_until_parked();
13375
13376    cx.assert_state_with_diff(
13377        r#"
13378        use some::mod1;
13379        use some::mod2;
13380
13381        const A: u32 = 42;
13382      + const B: u32 = 42;
13383      + const C: u32 = 42;
13384      + const D: u32 = 42;
13385      + const E: u32 = 42;
13386      + ˇ
13387
13388        fn main() {
13389            println!("hello");
13390
13391            println!("world");
13392        }
13393      "#
13394        .unindent(),
13395    );
13396
13397    cx.update_editor(|editor, window, cx| {
13398        editor.delete_line(&DeleteLine, window, cx);
13399    });
13400    executor.run_until_parked();
13401
13402    cx.assert_state_with_diff(
13403        r#"
13404        use some::mod1;
13405        use some::mod2;
13406
13407        const A: u32 = 42;
13408      + const B: u32 = 42;
13409      + const C: u32 = 42;
13410      + const D: u32 = 42;
13411      + const E: u32 = 42;
13412        ˇ
13413        fn main() {
13414            println!("hello");
13415
13416            println!("world");
13417        }
13418      "#
13419        .unindent(),
13420    );
13421
13422    cx.update_editor(|editor, window, cx| {
13423        editor.move_up(&MoveUp, window, cx);
13424        editor.delete_line(&DeleteLine, window, cx);
13425        editor.move_up(&MoveUp, window, cx);
13426        editor.delete_line(&DeleteLine, window, cx);
13427        editor.move_up(&MoveUp, window, cx);
13428        editor.delete_line(&DeleteLine, window, cx);
13429    });
13430    executor.run_until_parked();
13431    cx.assert_state_with_diff(
13432        r#"
13433        use some::mod1;
13434        use some::mod2;
13435
13436        const A: u32 = 42;
13437      + const B: u32 = 42;
13438        ˇ
13439        fn main() {
13440            println!("hello");
13441
13442            println!("world");
13443        }
13444      "#
13445        .unindent(),
13446    );
13447
13448    cx.update_editor(|editor, window, cx| {
13449        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13450        editor.delete_line(&DeleteLine, window, cx);
13451    });
13452    executor.run_until_parked();
13453    cx.assert_state_with_diff(
13454        r#"
13455        ˇ
13456        fn main() {
13457            println!("hello");
13458
13459            println!("world");
13460        }
13461      "#
13462        .unindent(),
13463    );
13464}
13465
13466#[gpui::test]
13467async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13468    init_test(cx, |_| {});
13469
13470    let mut cx = EditorTestContext::new(cx).await;
13471    cx.set_diff_base(indoc! { "
13472        one
13473        two
13474        three
13475        four
13476        five
13477        "
13478    });
13479    cx.set_state(indoc! { "
13480        one
13481        ˇthree
13482        five
13483    "});
13484    cx.run_until_parked();
13485    cx.update_editor(|editor, window, cx| {
13486        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13487    });
13488    cx.assert_state_with_diff(
13489        indoc! { "
13490        one
13491      - two
13492        ˇthree
13493      - four
13494        five
13495    "}
13496        .to_string(),
13497    );
13498    cx.update_editor(|editor, window, cx| {
13499        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13500    });
13501
13502    cx.assert_state_with_diff(
13503        indoc! { "
13504        one
13505        ˇthree
13506        five
13507    "}
13508        .to_string(),
13509    );
13510
13511    cx.set_state(indoc! { "
13512        one
13513        ˇTWO
13514        three
13515        four
13516        five
13517    "});
13518    cx.run_until_parked();
13519    cx.update_editor(|editor, window, cx| {
13520        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13521    });
13522
13523    cx.assert_state_with_diff(
13524        indoc! { "
13525            one
13526          - two
13527          + ˇTWO
13528            three
13529            four
13530            five
13531        "}
13532        .to_string(),
13533    );
13534    cx.update_editor(|editor, window, cx| {
13535        editor.move_up(&Default::default(), window, cx);
13536        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13537    });
13538    cx.assert_state_with_diff(
13539        indoc! { "
13540            one
13541            ˇTWO
13542            three
13543            four
13544            five
13545        "}
13546        .to_string(),
13547    );
13548}
13549
13550#[gpui::test]
13551async fn test_edits_around_expanded_deletion_hunks(
13552    executor: BackgroundExecutor,
13553    cx: &mut gpui::TestAppContext,
13554) {
13555    init_test(cx, |_| {});
13556
13557    let mut cx = EditorTestContext::new(cx).await;
13558
13559    let diff_base = r#"
13560        use some::mod1;
13561        use some::mod2;
13562
13563        const A: u32 = 42;
13564        const B: u32 = 42;
13565        const C: u32 = 42;
13566
13567
13568        fn main() {
13569            println!("hello");
13570
13571            println!("world");
13572        }
13573    "#
13574    .unindent();
13575    executor.run_until_parked();
13576    cx.set_state(
13577        &r#"
13578        use some::mod1;
13579        use some::mod2;
13580
13581        ˇconst B: u32 = 42;
13582        const C: u32 = 42;
13583
13584
13585        fn main() {
13586            println!("hello");
13587
13588            println!("world");
13589        }
13590        "#
13591        .unindent(),
13592    );
13593
13594    cx.set_diff_base(&diff_base);
13595    executor.run_until_parked();
13596
13597    cx.update_editor(|editor, window, cx| {
13598        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13599    });
13600    executor.run_until_parked();
13601
13602    cx.assert_state_with_diff(
13603        r#"
13604        use some::mod1;
13605        use some::mod2;
13606
13607      - const A: u32 = 42;
13608        ˇconst B: u32 = 42;
13609        const C: u32 = 42;
13610
13611
13612        fn main() {
13613            println!("hello");
13614
13615            println!("world");
13616        }
13617      "#
13618        .unindent(),
13619    );
13620
13621    cx.update_editor(|editor, window, cx| {
13622        editor.delete_line(&DeleteLine, window, cx);
13623    });
13624    executor.run_until_parked();
13625    cx.assert_state_with_diff(
13626        r#"
13627        use some::mod1;
13628        use some::mod2;
13629
13630      - const A: u32 = 42;
13631      - const B: u32 = 42;
13632        ˇconst C: u32 = 42;
13633
13634
13635        fn main() {
13636            println!("hello");
13637
13638            println!("world");
13639        }
13640      "#
13641        .unindent(),
13642    );
13643
13644    cx.update_editor(|editor, window, cx| {
13645        editor.delete_line(&DeleteLine, window, cx);
13646    });
13647    executor.run_until_parked();
13648    cx.assert_state_with_diff(
13649        r#"
13650        use some::mod1;
13651        use some::mod2;
13652
13653      - const A: u32 = 42;
13654      - const B: u32 = 42;
13655      - const C: u32 = 42;
13656        ˇ
13657
13658        fn main() {
13659            println!("hello");
13660
13661            println!("world");
13662        }
13663      "#
13664        .unindent(),
13665    );
13666
13667    cx.update_editor(|editor, window, cx| {
13668        editor.handle_input("replacement", window, cx);
13669    });
13670    executor.run_until_parked();
13671    cx.assert_state_with_diff(
13672        r#"
13673        use some::mod1;
13674        use some::mod2;
13675
13676      - const A: u32 = 42;
13677      - const B: u32 = 42;
13678      - const C: u32 = 42;
13679      -
13680      + replacementˇ
13681
13682        fn main() {
13683            println!("hello");
13684
13685            println!("world");
13686        }
13687      "#
13688        .unindent(),
13689    );
13690}
13691
13692#[gpui::test]
13693async fn test_backspace_after_deletion_hunk(
13694    executor: BackgroundExecutor,
13695    cx: &mut gpui::TestAppContext,
13696) {
13697    init_test(cx, |_| {});
13698
13699    let mut cx = EditorTestContext::new(cx).await;
13700
13701    let base_text = r#"
13702        one
13703        two
13704        three
13705        four
13706        five
13707    "#
13708    .unindent();
13709    executor.run_until_parked();
13710    cx.set_state(
13711        &r#"
13712        one
13713        two
13714        fˇour
13715        five
13716        "#
13717        .unindent(),
13718    );
13719
13720    cx.set_diff_base(&base_text);
13721    executor.run_until_parked();
13722
13723    cx.update_editor(|editor, window, cx| {
13724        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13725    });
13726    executor.run_until_parked();
13727
13728    cx.assert_state_with_diff(
13729        r#"
13730          one
13731          two
13732        - three
13733          fˇour
13734          five
13735        "#
13736        .unindent(),
13737    );
13738
13739    cx.update_editor(|editor, window, cx| {
13740        editor.backspace(&Backspace, window, cx);
13741        editor.backspace(&Backspace, window, cx);
13742    });
13743    executor.run_until_parked();
13744    cx.assert_state_with_diff(
13745        r#"
13746          one
13747          two
13748        - threeˇ
13749        - four
13750        + our
13751          five
13752        "#
13753        .unindent(),
13754    );
13755}
13756
13757#[gpui::test]
13758async fn test_edit_after_expanded_modification_hunk(
13759    executor: BackgroundExecutor,
13760    cx: &mut gpui::TestAppContext,
13761) {
13762    init_test(cx, |_| {});
13763
13764    let mut cx = EditorTestContext::new(cx).await;
13765
13766    let diff_base = r#"
13767        use some::mod1;
13768        use some::mod2;
13769
13770        const A: u32 = 42;
13771        const B: u32 = 42;
13772        const C: u32 = 42;
13773        const D: u32 = 42;
13774
13775
13776        fn main() {
13777            println!("hello");
13778
13779            println!("world");
13780        }"#
13781    .unindent();
13782
13783    cx.set_state(
13784        &r#"
13785        use some::mod1;
13786        use some::mod2;
13787
13788        const A: u32 = 42;
13789        const B: u32 = 42;
13790        const C: u32 = 43ˇ
13791        const D: u32 = 42;
13792
13793
13794        fn main() {
13795            println!("hello");
13796
13797            println!("world");
13798        }"#
13799        .unindent(),
13800    );
13801
13802    cx.set_diff_base(&diff_base);
13803    executor.run_until_parked();
13804    cx.update_editor(|editor, window, cx| {
13805        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13806    });
13807    executor.run_until_parked();
13808
13809    cx.assert_state_with_diff(
13810        r#"
13811        use some::mod1;
13812        use some::mod2;
13813
13814        const A: u32 = 42;
13815        const B: u32 = 42;
13816      - const C: u32 = 42;
13817      + const C: u32 = 43ˇ
13818        const D: u32 = 42;
13819
13820
13821        fn main() {
13822            println!("hello");
13823
13824            println!("world");
13825        }"#
13826        .unindent(),
13827    );
13828
13829    cx.update_editor(|editor, window, cx| {
13830        editor.handle_input("\nnew_line\n", window, cx);
13831    });
13832    executor.run_until_parked();
13833
13834    cx.assert_state_with_diff(
13835        r#"
13836        use some::mod1;
13837        use some::mod2;
13838
13839        const A: u32 = 42;
13840        const B: u32 = 42;
13841      - const C: u32 = 42;
13842      + const C: u32 = 43
13843      + new_line
13844      + ˇ
13845        const D: u32 = 42;
13846
13847
13848        fn main() {
13849            println!("hello");
13850
13851            println!("world");
13852        }"#
13853        .unindent(),
13854    );
13855}
13856
13857async fn setup_indent_guides_editor(
13858    text: &str,
13859    cx: &mut gpui::TestAppContext,
13860) -> (BufferId, EditorTestContext) {
13861    init_test(cx, |_| {});
13862
13863    let mut cx = EditorTestContext::new(cx).await;
13864
13865    let buffer_id = cx.update_editor(|editor, window, cx| {
13866        editor.set_text(text, window, cx);
13867        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13868
13869        buffer_ids[0]
13870    });
13871
13872    (buffer_id, cx)
13873}
13874
13875fn assert_indent_guides(
13876    range: Range<u32>,
13877    expected: Vec<IndentGuide>,
13878    active_indices: Option<Vec<usize>>,
13879    cx: &mut EditorTestContext,
13880) {
13881    let indent_guides = cx.update_editor(|editor, window, cx| {
13882        let snapshot = editor.snapshot(window, cx).display_snapshot;
13883        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13884            editor,
13885            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13886            true,
13887            &snapshot,
13888            cx,
13889        );
13890
13891        indent_guides.sort_by(|a, b| {
13892            a.depth.cmp(&b.depth).then(
13893                a.start_row
13894                    .cmp(&b.start_row)
13895                    .then(a.end_row.cmp(&b.end_row)),
13896            )
13897        });
13898        indent_guides
13899    });
13900
13901    if let Some(expected) = active_indices {
13902        let active_indices = cx.update_editor(|editor, window, cx| {
13903            let snapshot = editor.snapshot(window, cx).display_snapshot;
13904            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13905        });
13906
13907        assert_eq!(
13908            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13909            expected,
13910            "Active indent guide indices do not match"
13911        );
13912    }
13913
13914    assert_eq!(indent_guides, expected, "Indent guides do not match");
13915}
13916
13917fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13918    IndentGuide {
13919        buffer_id,
13920        start_row: MultiBufferRow(start_row),
13921        end_row: MultiBufferRow(end_row),
13922        depth,
13923        tab_size: 4,
13924        settings: IndentGuideSettings {
13925            enabled: true,
13926            line_width: 1,
13927            active_line_width: 1,
13928            ..Default::default()
13929        },
13930    }
13931}
13932
13933#[gpui::test]
13934async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13935    let (buffer_id, mut cx) = setup_indent_guides_editor(
13936        &"
13937    fn main() {
13938        let a = 1;
13939    }"
13940        .unindent(),
13941        cx,
13942    )
13943    .await;
13944
13945    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13946}
13947
13948#[gpui::test]
13949async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13950    let (buffer_id, mut cx) = setup_indent_guides_editor(
13951        &"
13952    fn main() {
13953        let a = 1;
13954        let b = 2;
13955    }"
13956        .unindent(),
13957        cx,
13958    )
13959    .await;
13960
13961    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13962}
13963
13964#[gpui::test]
13965async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13966    let (buffer_id, mut cx) = setup_indent_guides_editor(
13967        &"
13968    fn main() {
13969        let a = 1;
13970        if a == 3 {
13971            let b = 2;
13972        } else {
13973            let c = 3;
13974        }
13975    }"
13976        .unindent(),
13977        cx,
13978    )
13979    .await;
13980
13981    assert_indent_guides(
13982        0..8,
13983        vec![
13984            indent_guide(buffer_id, 1, 6, 0),
13985            indent_guide(buffer_id, 3, 3, 1),
13986            indent_guide(buffer_id, 5, 5, 1),
13987        ],
13988        None,
13989        &mut cx,
13990    );
13991}
13992
13993#[gpui::test]
13994async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13995    let (buffer_id, mut cx) = setup_indent_guides_editor(
13996        &"
13997    fn main() {
13998        let a = 1;
13999            let b = 2;
14000        let c = 3;
14001    }"
14002        .unindent(),
14003        cx,
14004    )
14005    .await;
14006
14007    assert_indent_guides(
14008        0..5,
14009        vec![
14010            indent_guide(buffer_id, 1, 3, 0),
14011            indent_guide(buffer_id, 2, 2, 1),
14012        ],
14013        None,
14014        &mut cx,
14015    );
14016}
14017
14018#[gpui::test]
14019async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14020    let (buffer_id, mut cx) = setup_indent_guides_editor(
14021        &"
14022        fn main() {
14023            let a = 1;
14024
14025            let c = 3;
14026        }"
14027        .unindent(),
14028        cx,
14029    )
14030    .await;
14031
14032    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14033}
14034
14035#[gpui::test]
14036async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14037    let (buffer_id, mut cx) = setup_indent_guides_editor(
14038        &"
14039        fn main() {
14040            let a = 1;
14041
14042            let c = 3;
14043
14044            if a == 3 {
14045                let b = 2;
14046            } else {
14047                let c = 3;
14048            }
14049        }"
14050        .unindent(),
14051        cx,
14052    )
14053    .await;
14054
14055    assert_indent_guides(
14056        0..11,
14057        vec![
14058            indent_guide(buffer_id, 1, 9, 0),
14059            indent_guide(buffer_id, 6, 6, 1),
14060            indent_guide(buffer_id, 8, 8, 1),
14061        ],
14062        None,
14063        &mut cx,
14064    );
14065}
14066
14067#[gpui::test]
14068async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14069    let (buffer_id, mut cx) = setup_indent_guides_editor(
14070        &"
14071        fn main() {
14072            let a = 1;
14073
14074            let c = 3;
14075
14076            if a == 3 {
14077                let b = 2;
14078            } else {
14079                let c = 3;
14080            }
14081        }"
14082        .unindent(),
14083        cx,
14084    )
14085    .await;
14086
14087    assert_indent_guides(
14088        1..11,
14089        vec![
14090            indent_guide(buffer_id, 1, 9, 0),
14091            indent_guide(buffer_id, 6, 6, 1),
14092            indent_guide(buffer_id, 8, 8, 1),
14093        ],
14094        None,
14095        &mut cx,
14096    );
14097}
14098
14099#[gpui::test]
14100async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14101    let (buffer_id, mut cx) = setup_indent_guides_editor(
14102        &"
14103        fn main() {
14104            let a = 1;
14105
14106            let c = 3;
14107
14108            if a == 3 {
14109                let b = 2;
14110            } else {
14111                let c = 3;
14112            }
14113        }"
14114        .unindent(),
14115        cx,
14116    )
14117    .await;
14118
14119    assert_indent_guides(
14120        1..10,
14121        vec![
14122            indent_guide(buffer_id, 1, 9, 0),
14123            indent_guide(buffer_id, 6, 6, 1),
14124            indent_guide(buffer_id, 8, 8, 1),
14125        ],
14126        None,
14127        &mut cx,
14128    );
14129}
14130
14131#[gpui::test]
14132async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14133    let (buffer_id, mut cx) = setup_indent_guides_editor(
14134        &"
14135        block1
14136            block2
14137                block3
14138                    block4
14139            block2
14140        block1
14141        block1"
14142            .unindent(),
14143        cx,
14144    )
14145    .await;
14146
14147    assert_indent_guides(
14148        1..10,
14149        vec![
14150            indent_guide(buffer_id, 1, 4, 0),
14151            indent_guide(buffer_id, 2, 3, 1),
14152            indent_guide(buffer_id, 3, 3, 2),
14153        ],
14154        None,
14155        &mut cx,
14156    );
14157}
14158
14159#[gpui::test]
14160async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14161    let (buffer_id, mut cx) = setup_indent_guides_editor(
14162        &"
14163        block1
14164            block2
14165                block3
14166
14167        block1
14168        block1"
14169            .unindent(),
14170        cx,
14171    )
14172    .await;
14173
14174    assert_indent_guides(
14175        0..6,
14176        vec![
14177            indent_guide(buffer_id, 1, 2, 0),
14178            indent_guide(buffer_id, 2, 2, 1),
14179        ],
14180        None,
14181        &mut cx,
14182    );
14183}
14184
14185#[gpui::test]
14186async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14187    let (buffer_id, mut cx) = setup_indent_guides_editor(
14188        &"
14189        block1
14190
14191
14192
14193            block2
14194        "
14195        .unindent(),
14196        cx,
14197    )
14198    .await;
14199
14200    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14201}
14202
14203#[gpui::test]
14204async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14205    let (buffer_id, mut cx) = setup_indent_guides_editor(
14206        &"
14207        def a:
14208        \tb = 3
14209        \tif True:
14210        \t\tc = 4
14211        \t\td = 5
14212        \tprint(b)
14213        "
14214        .unindent(),
14215        cx,
14216    )
14217    .await;
14218
14219    assert_indent_guides(
14220        0..6,
14221        vec![
14222            indent_guide(buffer_id, 1, 6, 0),
14223            indent_guide(buffer_id, 3, 4, 1),
14224        ],
14225        None,
14226        &mut cx,
14227    );
14228}
14229
14230#[gpui::test]
14231async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14232    let (buffer_id, mut cx) = setup_indent_guides_editor(
14233        &"
14234    fn main() {
14235        let a = 1;
14236    }"
14237        .unindent(),
14238        cx,
14239    )
14240    .await;
14241
14242    cx.update_editor(|editor, window, cx| {
14243        editor.change_selections(None, window, cx, |s| {
14244            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14245        });
14246    });
14247
14248    assert_indent_guides(
14249        0..3,
14250        vec![indent_guide(buffer_id, 1, 1, 0)],
14251        Some(vec![0]),
14252        &mut cx,
14253    );
14254}
14255
14256#[gpui::test]
14257async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14258    let (buffer_id, mut cx) = setup_indent_guides_editor(
14259        &"
14260    fn main() {
14261        if 1 == 2 {
14262            let a = 1;
14263        }
14264    }"
14265        .unindent(),
14266        cx,
14267    )
14268    .await;
14269
14270    cx.update_editor(|editor, window, cx| {
14271        editor.change_selections(None, window, cx, |s| {
14272            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14273        });
14274    });
14275
14276    assert_indent_guides(
14277        0..4,
14278        vec![
14279            indent_guide(buffer_id, 1, 3, 0),
14280            indent_guide(buffer_id, 2, 2, 1),
14281        ],
14282        Some(vec![1]),
14283        &mut cx,
14284    );
14285
14286    cx.update_editor(|editor, window, cx| {
14287        editor.change_selections(None, window, cx, |s| {
14288            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14289        });
14290    });
14291
14292    assert_indent_guides(
14293        0..4,
14294        vec![
14295            indent_guide(buffer_id, 1, 3, 0),
14296            indent_guide(buffer_id, 2, 2, 1),
14297        ],
14298        Some(vec![1]),
14299        &mut cx,
14300    );
14301
14302    cx.update_editor(|editor, window, cx| {
14303        editor.change_selections(None, window, cx, |s| {
14304            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14305        });
14306    });
14307
14308    assert_indent_guides(
14309        0..4,
14310        vec![
14311            indent_guide(buffer_id, 1, 3, 0),
14312            indent_guide(buffer_id, 2, 2, 1),
14313        ],
14314        Some(vec![0]),
14315        &mut cx,
14316    );
14317}
14318
14319#[gpui::test]
14320async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14321    let (buffer_id, mut cx) = setup_indent_guides_editor(
14322        &"
14323    fn main() {
14324        let a = 1;
14325
14326        let b = 2;
14327    }"
14328        .unindent(),
14329        cx,
14330    )
14331    .await;
14332
14333    cx.update_editor(|editor, window, cx| {
14334        editor.change_selections(None, window, cx, |s| {
14335            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14336        });
14337    });
14338
14339    assert_indent_guides(
14340        0..5,
14341        vec![indent_guide(buffer_id, 1, 3, 0)],
14342        Some(vec![0]),
14343        &mut cx,
14344    );
14345}
14346
14347#[gpui::test]
14348async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14349    let (buffer_id, mut cx) = setup_indent_guides_editor(
14350        &"
14351    def m:
14352        a = 1
14353        pass"
14354            .unindent(),
14355        cx,
14356    )
14357    .await;
14358
14359    cx.update_editor(|editor, window, cx| {
14360        editor.change_selections(None, window, cx, |s| {
14361            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14362        });
14363    });
14364
14365    assert_indent_guides(
14366        0..3,
14367        vec![indent_guide(buffer_id, 1, 2, 0)],
14368        Some(vec![0]),
14369        &mut cx,
14370    );
14371}
14372
14373#[gpui::test]
14374async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14375    init_test(cx, |_| {});
14376    let mut cx = EditorTestContext::new(cx).await;
14377    let text = indoc! {
14378        "
14379        impl A {
14380            fn b() {
14381                0;
14382                3;
14383                5;
14384                6;
14385                7;
14386            }
14387        }
14388        "
14389    };
14390    let base_text = indoc! {
14391        "
14392        impl A {
14393            fn b() {
14394                0;
14395                1;
14396                2;
14397                3;
14398                4;
14399            }
14400            fn c() {
14401                5;
14402                6;
14403                7;
14404            }
14405        }
14406        "
14407    };
14408
14409    cx.update_editor(|editor, window, cx| {
14410        editor.set_text(text, window, cx);
14411
14412        editor.buffer().update(cx, |multibuffer, cx| {
14413            let buffer = multibuffer.as_singleton().unwrap();
14414            let change_set =
14415                cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
14416
14417            multibuffer.set_all_diff_hunks_expanded(cx);
14418            multibuffer.add_change_set(change_set, cx);
14419
14420            buffer.read(cx).remote_id()
14421        })
14422    });
14423    cx.run_until_parked();
14424
14425    cx.assert_state_with_diff(
14426        indoc! { "
14427          impl A {
14428              fn b() {
14429                  0;
14430        -         1;
14431        -         2;
14432                  3;
14433        -         4;
14434        -     }
14435        -     fn c() {
14436                  5;
14437                  6;
14438                  7;
14439              }
14440          }
14441          ˇ"
14442        }
14443        .to_string(),
14444    );
14445
14446    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14447        editor
14448            .snapshot(window, cx)
14449            .buffer_snapshot
14450            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14451            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14452            .collect::<Vec<_>>()
14453    });
14454    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14455    assert_eq!(
14456        actual_guides,
14457        vec![
14458            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14459            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14460            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14461        ]
14462    );
14463}
14464
14465#[gpui::test]
14466fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14467    init_test(cx, |_| {});
14468
14469    let editor = cx.add_window(|window, cx| {
14470        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14471        build_editor(buffer, window, cx)
14472    });
14473
14474    let render_args = Arc::new(Mutex::new(None));
14475    let snapshot = editor
14476        .update(cx, |editor, window, cx| {
14477            let snapshot = editor.buffer().read(cx).snapshot(cx);
14478            let range =
14479                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14480
14481            struct RenderArgs {
14482                row: MultiBufferRow,
14483                folded: bool,
14484                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14485            }
14486
14487            let crease = Crease::inline(
14488                range,
14489                FoldPlaceholder::test(),
14490                {
14491                    let toggle_callback = render_args.clone();
14492                    move |row, folded, callback, _window, _cx| {
14493                        *toggle_callback.lock() = Some(RenderArgs {
14494                            row,
14495                            folded,
14496                            callback,
14497                        });
14498                        div()
14499                    }
14500                },
14501                |_row, _folded, _window, _cx| div(),
14502            );
14503
14504            editor.insert_creases(Some(crease), cx);
14505            let snapshot = editor.snapshot(window, cx);
14506            let _div = snapshot.render_crease_toggle(
14507                MultiBufferRow(1),
14508                false,
14509                cx.entity().clone(),
14510                window,
14511                cx,
14512            );
14513            snapshot
14514        })
14515        .unwrap();
14516
14517    let render_args = render_args.lock().take().unwrap();
14518    assert_eq!(render_args.row, MultiBufferRow(1));
14519    assert!(!render_args.folded);
14520    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14521
14522    cx.update_window(*editor, |_, window, cx| {
14523        (render_args.callback)(true, window, cx)
14524    })
14525    .unwrap();
14526    let snapshot = editor
14527        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14528        .unwrap();
14529    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14530
14531    cx.update_window(*editor, |_, window, cx| {
14532        (render_args.callback)(false, window, cx)
14533    })
14534    .unwrap();
14535    let snapshot = editor
14536        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14537        .unwrap();
14538    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14539}
14540
14541#[gpui::test]
14542async fn test_input_text(cx: &mut gpui::TestAppContext) {
14543    init_test(cx, |_| {});
14544    let mut cx = EditorTestContext::new(cx).await;
14545
14546    cx.set_state(
14547        &r#"ˇone
14548        two
14549
14550        three
14551        fourˇ
14552        five
14553
14554        siˇx"#
14555            .unindent(),
14556    );
14557
14558    cx.dispatch_action(HandleInput(String::new()));
14559    cx.assert_editor_state(
14560        &r#"ˇone
14561        two
14562
14563        three
14564        fourˇ
14565        five
14566
14567        siˇx"#
14568            .unindent(),
14569    );
14570
14571    cx.dispatch_action(HandleInput("AAAA".to_string()));
14572    cx.assert_editor_state(
14573        &r#"AAAAˇone
14574        two
14575
14576        three
14577        fourAAAAˇ
14578        five
14579
14580        siAAAAˇx"#
14581            .unindent(),
14582    );
14583}
14584
14585#[gpui::test]
14586async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14587    init_test(cx, |_| {});
14588
14589    let mut cx = EditorTestContext::new(cx).await;
14590    cx.set_state(
14591        r#"let foo = 1;
14592let foo = 2;
14593let foo = 3;
14594let fooˇ = 4;
14595let foo = 5;
14596let foo = 6;
14597let foo = 7;
14598let foo = 8;
14599let foo = 9;
14600let foo = 10;
14601let foo = 11;
14602let foo = 12;
14603let foo = 13;
14604let foo = 14;
14605let foo = 15;"#,
14606    );
14607
14608    cx.update_editor(|e, window, cx| {
14609        assert_eq!(
14610            e.next_scroll_position,
14611            NextScrollCursorCenterTopBottom::Center,
14612            "Default next scroll direction is center",
14613        );
14614
14615        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14616        assert_eq!(
14617            e.next_scroll_position,
14618            NextScrollCursorCenterTopBottom::Top,
14619            "After center, next scroll direction should be top",
14620        );
14621
14622        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14623        assert_eq!(
14624            e.next_scroll_position,
14625            NextScrollCursorCenterTopBottom::Bottom,
14626            "After top, next scroll direction should be bottom",
14627        );
14628
14629        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14630        assert_eq!(
14631            e.next_scroll_position,
14632            NextScrollCursorCenterTopBottom::Center,
14633            "After bottom, scrolling should start over",
14634        );
14635
14636        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14637        assert_eq!(
14638            e.next_scroll_position,
14639            NextScrollCursorCenterTopBottom::Top,
14640            "Scrolling continues if retriggered fast enough"
14641        );
14642    });
14643
14644    cx.executor()
14645        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14646    cx.executor().run_until_parked();
14647    cx.update_editor(|e, _, _| {
14648        assert_eq!(
14649            e.next_scroll_position,
14650            NextScrollCursorCenterTopBottom::Center,
14651            "If scrolling is not triggered fast enough, it should reset"
14652        );
14653    });
14654}
14655
14656#[gpui::test]
14657async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14658    init_test(cx, |_| {});
14659    let mut cx = EditorLspTestContext::new_rust(
14660        lsp::ServerCapabilities {
14661            definition_provider: Some(lsp::OneOf::Left(true)),
14662            references_provider: Some(lsp::OneOf::Left(true)),
14663            ..lsp::ServerCapabilities::default()
14664        },
14665        cx,
14666    )
14667    .await;
14668
14669    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14670        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14671            move |params, _| async move {
14672                if empty_go_to_definition {
14673                    Ok(None)
14674                } else {
14675                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14676                        uri: params.text_document_position_params.text_document.uri,
14677                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14678                    })))
14679                }
14680            },
14681        );
14682        let references =
14683            cx.lsp
14684                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14685                    Ok(Some(vec![lsp::Location {
14686                        uri: params.text_document_position.text_document.uri,
14687                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14688                    }]))
14689                });
14690        (go_to_definition, references)
14691    };
14692
14693    cx.set_state(
14694        &r#"fn one() {
14695            let mut a = ˇtwo();
14696        }
14697
14698        fn two() {}"#
14699            .unindent(),
14700    );
14701    set_up_lsp_handlers(false, &mut cx);
14702    let navigated = cx
14703        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14704        .await
14705        .expect("Failed to navigate to definition");
14706    assert_eq!(
14707        navigated,
14708        Navigated::Yes,
14709        "Should have navigated to definition from the GetDefinition response"
14710    );
14711    cx.assert_editor_state(
14712        &r#"fn one() {
14713            let mut a = two();
14714        }
14715
14716        fn «twoˇ»() {}"#
14717            .unindent(),
14718    );
14719
14720    let editors = cx.update_workspace(|workspace, _, cx| {
14721        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14722    });
14723    cx.update_editor(|_, _, test_editor_cx| {
14724        assert_eq!(
14725            editors.len(),
14726            1,
14727            "Initially, only one, test, editor should be open in the workspace"
14728        );
14729        assert_eq!(
14730            test_editor_cx.entity(),
14731            editors.last().expect("Asserted len is 1").clone()
14732        );
14733    });
14734
14735    set_up_lsp_handlers(true, &mut cx);
14736    let navigated = cx
14737        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14738        .await
14739        .expect("Failed to navigate to lookup references");
14740    assert_eq!(
14741        navigated,
14742        Navigated::Yes,
14743        "Should have navigated to references as a fallback after empty GoToDefinition response"
14744    );
14745    // We should not change the selections in the existing file,
14746    // if opening another milti buffer with the references
14747    cx.assert_editor_state(
14748        &r#"fn one() {
14749            let mut a = two();
14750        }
14751
14752        fn «twoˇ»() {}"#
14753            .unindent(),
14754    );
14755    let editors = cx.update_workspace(|workspace, _, cx| {
14756        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14757    });
14758    cx.update_editor(|_, _, test_editor_cx| {
14759        assert_eq!(
14760            editors.len(),
14761            2,
14762            "After falling back to references search, we open a new editor with the results"
14763        );
14764        let references_fallback_text = editors
14765            .into_iter()
14766            .find(|new_editor| *new_editor != test_editor_cx.entity())
14767            .expect("Should have one non-test editor now")
14768            .read(test_editor_cx)
14769            .text(test_editor_cx);
14770        assert_eq!(
14771            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14772            "Should use the range from the references response and not the GoToDefinition one"
14773        );
14774    });
14775}
14776
14777#[gpui::test]
14778async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14779    init_test(cx, |_| {});
14780
14781    let language = Arc::new(Language::new(
14782        LanguageConfig::default(),
14783        Some(tree_sitter_rust::LANGUAGE.into()),
14784    ));
14785
14786    let text = r#"
14787        #[cfg(test)]
14788        mod tests() {
14789            #[test]
14790            fn runnable_1() {
14791                let a = 1;
14792            }
14793
14794            #[test]
14795            fn runnable_2() {
14796                let a = 1;
14797                let b = 2;
14798            }
14799        }
14800    "#
14801    .unindent();
14802
14803    let fs = FakeFs::new(cx.executor());
14804    fs.insert_file("/file.rs", Default::default()).await;
14805
14806    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14807    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14808    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14809    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14810    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14811
14812    let editor = cx.new_window_entity(|window, cx| {
14813        Editor::new(
14814            EditorMode::Full,
14815            multi_buffer,
14816            Some(project.clone()),
14817            true,
14818            window,
14819            cx,
14820        )
14821    });
14822
14823    editor.update_in(cx, |editor, window, cx| {
14824        editor.tasks.insert(
14825            (buffer.read(cx).remote_id(), 3),
14826            RunnableTasks {
14827                templates: vec![],
14828                offset: MultiBufferOffset(43),
14829                column: 0,
14830                extra_variables: HashMap::default(),
14831                context_range: BufferOffset(43)..BufferOffset(85),
14832            },
14833        );
14834        editor.tasks.insert(
14835            (buffer.read(cx).remote_id(), 8),
14836            RunnableTasks {
14837                templates: vec![],
14838                offset: MultiBufferOffset(86),
14839                column: 0,
14840                extra_variables: HashMap::default(),
14841                context_range: BufferOffset(86)..BufferOffset(191),
14842            },
14843        );
14844
14845        // Test finding task when cursor is inside function body
14846        editor.change_selections(None, window, cx, |s| {
14847            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14848        });
14849        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14850        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14851
14852        // Test finding task when cursor is on function name
14853        editor.change_selections(None, window, cx, |s| {
14854            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14855        });
14856        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14857        assert_eq!(row, 8, "Should find task when cursor is on function name");
14858    });
14859}
14860
14861#[gpui::test]
14862async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14863    init_test(cx, |_| {});
14864
14865    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14866    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14867    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14868
14869    let fs = FakeFs::new(cx.executor());
14870    fs.insert_tree(
14871        "/a",
14872        json!({
14873            "first.rs": sample_text_1,
14874            "second.rs": sample_text_2,
14875            "third.rs": sample_text_3,
14876        }),
14877    )
14878    .await;
14879    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14880    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14881    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14882    let worktree = project.update(cx, |project, cx| {
14883        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14884        assert_eq!(worktrees.len(), 1);
14885        worktrees.pop().unwrap()
14886    });
14887    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14888
14889    let buffer_1 = project
14890        .update(cx, |project, cx| {
14891            project.open_buffer((worktree_id, "first.rs"), cx)
14892        })
14893        .await
14894        .unwrap();
14895    let buffer_2 = project
14896        .update(cx, |project, cx| {
14897            project.open_buffer((worktree_id, "second.rs"), cx)
14898        })
14899        .await
14900        .unwrap();
14901    let buffer_3 = project
14902        .update(cx, |project, cx| {
14903            project.open_buffer((worktree_id, "third.rs"), cx)
14904        })
14905        .await
14906        .unwrap();
14907
14908    let multi_buffer = cx.new(|cx| {
14909        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14910        multi_buffer.push_excerpts(
14911            buffer_1.clone(),
14912            [
14913                ExcerptRange {
14914                    context: Point::new(0, 0)..Point::new(3, 0),
14915                    primary: None,
14916                },
14917                ExcerptRange {
14918                    context: Point::new(5, 0)..Point::new(7, 0),
14919                    primary: None,
14920                },
14921                ExcerptRange {
14922                    context: Point::new(9, 0)..Point::new(10, 4),
14923                    primary: None,
14924                },
14925            ],
14926            cx,
14927        );
14928        multi_buffer.push_excerpts(
14929            buffer_2.clone(),
14930            [
14931                ExcerptRange {
14932                    context: Point::new(0, 0)..Point::new(3, 0),
14933                    primary: None,
14934                },
14935                ExcerptRange {
14936                    context: Point::new(5, 0)..Point::new(7, 0),
14937                    primary: None,
14938                },
14939                ExcerptRange {
14940                    context: Point::new(9, 0)..Point::new(10, 4),
14941                    primary: None,
14942                },
14943            ],
14944            cx,
14945        );
14946        multi_buffer.push_excerpts(
14947            buffer_3.clone(),
14948            [
14949                ExcerptRange {
14950                    context: Point::new(0, 0)..Point::new(3, 0),
14951                    primary: None,
14952                },
14953                ExcerptRange {
14954                    context: Point::new(5, 0)..Point::new(7, 0),
14955                    primary: None,
14956                },
14957                ExcerptRange {
14958                    context: Point::new(9, 0)..Point::new(10, 4),
14959                    primary: None,
14960                },
14961            ],
14962            cx,
14963        );
14964        multi_buffer
14965    });
14966    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14967        Editor::new(
14968            EditorMode::Full,
14969            multi_buffer,
14970            Some(project.clone()),
14971            true,
14972            window,
14973            cx,
14974        )
14975    });
14976
14977    let full_text = "\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";
14978    assert_eq!(
14979        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14980        full_text,
14981    );
14982
14983    multi_buffer_editor.update(cx, |editor, cx| {
14984        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14985    });
14986    assert_eq!(
14987        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14988        "\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",
14989        "After folding the first buffer, its text should not be displayed"
14990    );
14991
14992    multi_buffer_editor.update(cx, |editor, cx| {
14993        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14994    });
14995    assert_eq!(
14996        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14997        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14998        "After folding the second buffer, its text should not be displayed"
14999    );
15000
15001    multi_buffer_editor.update(cx, |editor, cx| {
15002        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15003    });
15004    assert_eq!(
15005        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15006        "\n\n\n\n\n",
15007        "After folding the third buffer, its text should not be displayed"
15008    );
15009
15010    // Emulate selection inside the fold logic, that should work
15011    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15012        editor
15013            .snapshot(window, cx)
15014            .next_line_boundary(Point::new(0, 4));
15015    });
15016
15017    multi_buffer_editor.update(cx, |editor, cx| {
15018        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15019    });
15020    assert_eq!(
15021        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15022        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15023        "After unfolding the second buffer, its text should be displayed"
15024    );
15025
15026    multi_buffer_editor.update(cx, |editor, cx| {
15027        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15028    });
15029    assert_eq!(
15030        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15031        "\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",
15032        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15033    );
15034
15035    multi_buffer_editor.update(cx, |editor, cx| {
15036        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15037    });
15038    assert_eq!(
15039        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15040        full_text,
15041        "After unfolding the all buffers, all original text should be displayed"
15042    );
15043}
15044
15045#[gpui::test]
15046async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15047    init_test(cx, |_| {});
15048
15049    let sample_text_1 = "1111\n2222\n3333".to_string();
15050    let sample_text_2 = "4444\n5555\n6666".to_string();
15051    let sample_text_3 = "7777\n8888\n9999".to_string();
15052
15053    let fs = FakeFs::new(cx.executor());
15054    fs.insert_tree(
15055        "/a",
15056        json!({
15057            "first.rs": sample_text_1,
15058            "second.rs": sample_text_2,
15059            "third.rs": sample_text_3,
15060        }),
15061    )
15062    .await;
15063    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15064    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15065    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15066    let worktree = project.update(cx, |project, cx| {
15067        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15068        assert_eq!(worktrees.len(), 1);
15069        worktrees.pop().unwrap()
15070    });
15071    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15072
15073    let buffer_1 = project
15074        .update(cx, |project, cx| {
15075            project.open_buffer((worktree_id, "first.rs"), cx)
15076        })
15077        .await
15078        .unwrap();
15079    let buffer_2 = project
15080        .update(cx, |project, cx| {
15081            project.open_buffer((worktree_id, "second.rs"), cx)
15082        })
15083        .await
15084        .unwrap();
15085    let buffer_3 = project
15086        .update(cx, |project, cx| {
15087            project.open_buffer((worktree_id, "third.rs"), cx)
15088        })
15089        .await
15090        .unwrap();
15091
15092    let multi_buffer = cx.new(|cx| {
15093        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15094        multi_buffer.push_excerpts(
15095            buffer_1.clone(),
15096            [ExcerptRange {
15097                context: Point::new(0, 0)..Point::new(3, 0),
15098                primary: None,
15099            }],
15100            cx,
15101        );
15102        multi_buffer.push_excerpts(
15103            buffer_2.clone(),
15104            [ExcerptRange {
15105                context: Point::new(0, 0)..Point::new(3, 0),
15106                primary: None,
15107            }],
15108            cx,
15109        );
15110        multi_buffer.push_excerpts(
15111            buffer_3.clone(),
15112            [ExcerptRange {
15113                context: Point::new(0, 0)..Point::new(3, 0),
15114                primary: None,
15115            }],
15116            cx,
15117        );
15118        multi_buffer
15119    });
15120
15121    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15122        Editor::new(
15123            EditorMode::Full,
15124            multi_buffer,
15125            Some(project.clone()),
15126            true,
15127            window,
15128            cx,
15129        )
15130    });
15131
15132    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15133    assert_eq!(
15134        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15135        full_text,
15136    );
15137
15138    multi_buffer_editor.update(cx, |editor, cx| {
15139        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15140    });
15141    assert_eq!(
15142        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15143        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15144        "After folding the first buffer, its text should not be displayed"
15145    );
15146
15147    multi_buffer_editor.update(cx, |editor, cx| {
15148        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15149    });
15150
15151    assert_eq!(
15152        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15153        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15154        "After folding the second buffer, its text should not be displayed"
15155    );
15156
15157    multi_buffer_editor.update(cx, |editor, cx| {
15158        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15159    });
15160    assert_eq!(
15161        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15162        "\n\n\n\n\n",
15163        "After folding the third buffer, its text should not be displayed"
15164    );
15165
15166    multi_buffer_editor.update(cx, |editor, cx| {
15167        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15168    });
15169    assert_eq!(
15170        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15171        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15172        "After unfolding the second buffer, its text should be displayed"
15173    );
15174
15175    multi_buffer_editor.update(cx, |editor, cx| {
15176        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15177    });
15178    assert_eq!(
15179        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15180        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15181        "After unfolding the first buffer, its text should be displayed"
15182    );
15183
15184    multi_buffer_editor.update(cx, |editor, cx| {
15185        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15186    });
15187    assert_eq!(
15188        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15189        full_text,
15190        "After unfolding all buffers, all original text should be displayed"
15191    );
15192}
15193
15194#[gpui::test]
15195async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15196    init_test(cx, |_| {});
15197
15198    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15199
15200    let fs = FakeFs::new(cx.executor());
15201    fs.insert_tree(
15202        "/a",
15203        json!({
15204            "main.rs": sample_text,
15205        }),
15206    )
15207    .await;
15208    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15209    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15210    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15211    let worktree = project.update(cx, |project, cx| {
15212        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15213        assert_eq!(worktrees.len(), 1);
15214        worktrees.pop().unwrap()
15215    });
15216    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15217
15218    let buffer_1 = project
15219        .update(cx, |project, cx| {
15220            project.open_buffer((worktree_id, "main.rs"), cx)
15221        })
15222        .await
15223        .unwrap();
15224
15225    let multi_buffer = cx.new(|cx| {
15226        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15227        multi_buffer.push_excerpts(
15228            buffer_1.clone(),
15229            [ExcerptRange {
15230                context: Point::new(0, 0)
15231                    ..Point::new(
15232                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15233                        0,
15234                    ),
15235                primary: None,
15236            }],
15237            cx,
15238        );
15239        multi_buffer
15240    });
15241    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15242        Editor::new(
15243            EditorMode::Full,
15244            multi_buffer,
15245            Some(project.clone()),
15246            true,
15247            window,
15248            cx,
15249        )
15250    });
15251
15252    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15253    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15254        enum TestHighlight {}
15255        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15256        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15257        editor.highlight_text::<TestHighlight>(
15258            vec![highlight_range.clone()],
15259            HighlightStyle::color(Hsla::green()),
15260            cx,
15261        );
15262        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15263    });
15264
15265    let full_text = format!("\n\n\n{sample_text}\n");
15266    assert_eq!(
15267        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15268        full_text,
15269    );
15270}
15271
15272#[gpui::test]
15273async fn test_inline_completion_text(cx: &mut TestAppContext) {
15274    init_test(cx, |_| {});
15275
15276    // Simple insertion
15277    assert_highlighted_edits(
15278        "Hello, world!",
15279        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15280        true,
15281        cx,
15282        |highlighted_edits, cx| {
15283            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15284            assert_eq!(highlighted_edits.highlights.len(), 1);
15285            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15286            assert_eq!(
15287                highlighted_edits.highlights[0].1.background_color,
15288                Some(cx.theme().status().created_background)
15289            );
15290        },
15291    )
15292    .await;
15293
15294    // Replacement
15295    assert_highlighted_edits(
15296        "This is a test.",
15297        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15298        false,
15299        cx,
15300        |highlighted_edits, cx| {
15301            assert_eq!(highlighted_edits.text, "That is a test.");
15302            assert_eq!(highlighted_edits.highlights.len(), 1);
15303            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15304            assert_eq!(
15305                highlighted_edits.highlights[0].1.background_color,
15306                Some(cx.theme().status().created_background)
15307            );
15308        },
15309    )
15310    .await;
15311
15312    // Multiple edits
15313    assert_highlighted_edits(
15314        "Hello, world!",
15315        vec![
15316            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15317            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15318        ],
15319        false,
15320        cx,
15321        |highlighted_edits, cx| {
15322            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15323            assert_eq!(highlighted_edits.highlights.len(), 2);
15324            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15325            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15326            assert_eq!(
15327                highlighted_edits.highlights[0].1.background_color,
15328                Some(cx.theme().status().created_background)
15329            );
15330            assert_eq!(
15331                highlighted_edits.highlights[1].1.background_color,
15332                Some(cx.theme().status().created_background)
15333            );
15334        },
15335    )
15336    .await;
15337
15338    // Multiple lines with edits
15339    assert_highlighted_edits(
15340        "First line\nSecond line\nThird line\nFourth line",
15341        vec![
15342            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15343            (
15344                Point::new(2, 0)..Point::new(2, 10),
15345                "New third line".to_string(),
15346            ),
15347            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15348        ],
15349        false,
15350        cx,
15351        |highlighted_edits, cx| {
15352            assert_eq!(
15353                highlighted_edits.text,
15354                "Second modified\nNew third line\nFourth updated line"
15355            );
15356            assert_eq!(highlighted_edits.highlights.len(), 3);
15357            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15358            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15359            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15360            for highlight in &highlighted_edits.highlights {
15361                assert_eq!(
15362                    highlight.1.background_color,
15363                    Some(cx.theme().status().created_background)
15364                );
15365            }
15366        },
15367    )
15368    .await;
15369}
15370
15371#[gpui::test]
15372async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15373    init_test(cx, |_| {});
15374
15375    // Deletion
15376    assert_highlighted_edits(
15377        "Hello, world!",
15378        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15379        true,
15380        cx,
15381        |highlighted_edits, cx| {
15382            assert_eq!(highlighted_edits.text, "Hello, world!");
15383            assert_eq!(highlighted_edits.highlights.len(), 1);
15384            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15385            assert_eq!(
15386                highlighted_edits.highlights[0].1.background_color,
15387                Some(cx.theme().status().deleted_background)
15388            );
15389        },
15390    )
15391    .await;
15392
15393    // Insertion
15394    assert_highlighted_edits(
15395        "Hello, world!",
15396        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15397        true,
15398        cx,
15399        |highlighted_edits, cx| {
15400            assert_eq!(highlighted_edits.highlights.len(), 1);
15401            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15402            assert_eq!(
15403                highlighted_edits.highlights[0].1.background_color,
15404                Some(cx.theme().status().created_background)
15405            );
15406        },
15407    )
15408    .await;
15409}
15410
15411async fn assert_highlighted_edits(
15412    text: &str,
15413    edits: Vec<(Range<Point>, String)>,
15414    include_deletions: bool,
15415    cx: &mut TestAppContext,
15416    assertion_fn: impl Fn(HighlightedText, &App),
15417) {
15418    let window = cx.add_window(|window, cx| {
15419        let buffer = MultiBuffer::build_simple(text, cx);
15420        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15421    });
15422    let cx = &mut VisualTestContext::from_window(*window, cx);
15423
15424    let (buffer, snapshot) = window
15425        .update(cx, |editor, _window, cx| {
15426            (
15427                editor.buffer().clone(),
15428                editor.buffer().read(cx).snapshot(cx),
15429            )
15430        })
15431        .unwrap();
15432
15433    let edits = edits
15434        .into_iter()
15435        .map(|(range, edit)| {
15436            (
15437                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15438                edit,
15439            )
15440        })
15441        .collect::<Vec<_>>();
15442
15443    let text_anchor_edits = edits
15444        .clone()
15445        .into_iter()
15446        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15447        .collect::<Vec<_>>();
15448
15449    let edit_preview = window
15450        .update(cx, |_, _window, cx| {
15451            buffer
15452                .read(cx)
15453                .as_singleton()
15454                .unwrap()
15455                .read(cx)
15456                .preview_edits(text_anchor_edits.into(), cx)
15457        })
15458        .unwrap()
15459        .await;
15460
15461    cx.update(|_window, cx| {
15462        let highlighted_edits = inline_completion_edit_text(
15463            &snapshot.as_singleton().unwrap().2,
15464            &edits,
15465            &edit_preview,
15466            include_deletions,
15467            cx,
15468        );
15469        assertion_fn(highlighted_edits, cx)
15470    });
15471}
15472
15473#[gpui::test]
15474async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15475    init_test(cx, |_| {});
15476    let capabilities = lsp::ServerCapabilities {
15477        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15478            prepare_provider: Some(true),
15479            work_done_progress_options: Default::default(),
15480        })),
15481        ..Default::default()
15482    };
15483    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15484
15485    cx.set_state(indoc! {"
15486        struct Fˇoo {}
15487    "});
15488
15489    cx.update_editor(|editor, _, cx| {
15490        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15491        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15492        editor.highlight_background::<DocumentHighlightRead>(
15493            &[highlight_range],
15494            |c| c.editor_document_highlight_read_background,
15495            cx,
15496        );
15497    });
15498
15499    let mut prepare_rename_handler =
15500        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15501            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15502                start: lsp::Position {
15503                    line: 0,
15504                    character: 7,
15505                },
15506                end: lsp::Position {
15507                    line: 0,
15508                    character: 10,
15509                },
15510            })))
15511        });
15512    let prepare_rename_task = cx
15513        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15514        .expect("Prepare rename was not started");
15515    prepare_rename_handler.next().await.unwrap();
15516    prepare_rename_task.await.expect("Prepare rename failed");
15517
15518    let mut rename_handler =
15519        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15520            let edit = lsp::TextEdit {
15521                range: lsp::Range {
15522                    start: lsp::Position {
15523                        line: 0,
15524                        character: 7,
15525                    },
15526                    end: lsp::Position {
15527                        line: 0,
15528                        character: 10,
15529                    },
15530                },
15531                new_text: "FooRenamed".to_string(),
15532            };
15533            Ok(Some(lsp::WorkspaceEdit::new(
15534                // Specify the same edit twice
15535                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15536            )))
15537        });
15538    let rename_task = cx
15539        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15540        .expect("Confirm rename was not started");
15541    rename_handler.next().await.unwrap();
15542    rename_task.await.expect("Confirm rename failed");
15543    cx.run_until_parked();
15544
15545    // Despite two edits, only one is actually applied as those are identical
15546    cx.assert_editor_state(indoc! {"
15547        struct FooRenamedˇ {}
15548    "});
15549}
15550
15551#[gpui::test]
15552async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15553    init_test(cx, |_| {});
15554    // These capabilities indicate that the server does not support prepare rename.
15555    let capabilities = lsp::ServerCapabilities {
15556        rename_provider: Some(lsp::OneOf::Left(true)),
15557        ..Default::default()
15558    };
15559    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15560
15561    cx.set_state(indoc! {"
15562        struct Fˇoo {}
15563    "});
15564
15565    cx.update_editor(|editor, _window, cx| {
15566        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15567        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15568        editor.highlight_background::<DocumentHighlightRead>(
15569            &[highlight_range],
15570            |c| c.editor_document_highlight_read_background,
15571            cx,
15572        );
15573    });
15574
15575    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15576        .expect("Prepare rename was not started")
15577        .await
15578        .expect("Prepare rename failed");
15579
15580    let mut rename_handler =
15581        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15582            let edit = lsp::TextEdit {
15583                range: lsp::Range {
15584                    start: lsp::Position {
15585                        line: 0,
15586                        character: 7,
15587                    },
15588                    end: lsp::Position {
15589                        line: 0,
15590                        character: 10,
15591                    },
15592                },
15593                new_text: "FooRenamed".to_string(),
15594            };
15595            Ok(Some(lsp::WorkspaceEdit::new(
15596                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15597            )))
15598        });
15599    let rename_task = cx
15600        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15601        .expect("Confirm rename was not started");
15602    rename_handler.next().await.unwrap();
15603    rename_task.await.expect("Confirm rename failed");
15604    cx.run_until_parked();
15605
15606    // Correct range is renamed, as `surrounding_word` is used to find it.
15607    cx.assert_editor_state(indoc! {"
15608        struct FooRenamedˇ {}
15609    "});
15610}
15611
15612fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15613    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15614    point..point
15615}
15616
15617fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15618    let (text, ranges) = marked_text_ranges(marked_text, true);
15619    assert_eq!(editor.text(cx), text);
15620    assert_eq!(
15621        editor.selections.ranges(cx),
15622        ranges,
15623        "Assert selections are {}",
15624        marked_text
15625    );
15626}
15627
15628pub fn handle_signature_help_request(
15629    cx: &mut EditorLspTestContext,
15630    mocked_response: lsp::SignatureHelp,
15631) -> impl Future<Output = ()> {
15632    let mut request =
15633        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15634            let mocked_response = mocked_response.clone();
15635            async move { Ok(Some(mocked_response)) }
15636        });
15637
15638    async move {
15639        request.next().await;
15640    }
15641}
15642
15643/// Handle completion request passing a marked string specifying where the completion
15644/// should be triggered from using '|' character, what range should be replaced, and what completions
15645/// should be returned using '<' and '>' to delimit the range
15646pub fn handle_completion_request(
15647    cx: &mut EditorLspTestContext,
15648    marked_string: &str,
15649    completions: Vec<&'static str>,
15650    counter: Arc<AtomicUsize>,
15651) -> impl Future<Output = ()> {
15652    let complete_from_marker: TextRangeMarker = '|'.into();
15653    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15654    let (_, mut marked_ranges) = marked_text_ranges_by(
15655        marked_string,
15656        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15657    );
15658
15659    let complete_from_position =
15660        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15661    let replace_range =
15662        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15663
15664    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15665        let completions = completions.clone();
15666        counter.fetch_add(1, atomic::Ordering::Release);
15667        async move {
15668            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15669            assert_eq!(
15670                params.text_document_position.position,
15671                complete_from_position
15672            );
15673            Ok(Some(lsp::CompletionResponse::Array(
15674                completions
15675                    .iter()
15676                    .map(|completion_text| lsp::CompletionItem {
15677                        label: completion_text.to_string(),
15678                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15679                            range: replace_range,
15680                            new_text: completion_text.to_string(),
15681                        })),
15682                        ..Default::default()
15683                    })
15684                    .collect(),
15685            )))
15686        }
15687    });
15688
15689    async move {
15690        request.next().await;
15691    }
15692}
15693
15694fn handle_resolve_completion_request(
15695    cx: &mut EditorLspTestContext,
15696    edits: Option<Vec<(&'static str, &'static str)>>,
15697) -> impl Future<Output = ()> {
15698    let edits = edits.map(|edits| {
15699        edits
15700            .iter()
15701            .map(|(marked_string, new_text)| {
15702                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15703                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15704                lsp::TextEdit::new(replace_range, new_text.to_string())
15705            })
15706            .collect::<Vec<_>>()
15707    });
15708
15709    let mut request =
15710        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15711            let edits = edits.clone();
15712            async move {
15713                Ok(lsp::CompletionItem {
15714                    additional_text_edits: edits,
15715                    ..Default::default()
15716                })
15717            }
15718        });
15719
15720    async move {
15721        request.next().await;
15722    }
15723}
15724
15725pub(crate) fn update_test_language_settings(
15726    cx: &mut TestAppContext,
15727    f: impl Fn(&mut AllLanguageSettingsContent),
15728) {
15729    cx.update(|cx| {
15730        SettingsStore::update_global(cx, |store, cx| {
15731            store.update_user_settings::<AllLanguageSettings>(cx, f);
15732        });
15733    });
15734}
15735
15736pub(crate) fn update_test_project_settings(
15737    cx: &mut TestAppContext,
15738    f: impl Fn(&mut ProjectSettings),
15739) {
15740    cx.update(|cx| {
15741        SettingsStore::update_global(cx, |store, cx| {
15742            store.update_user_settings::<ProjectSettings>(cx, f);
15743        });
15744    });
15745}
15746
15747pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15748    cx.update(|cx| {
15749        assets::Assets.load_test_fonts(cx);
15750        let store = SettingsStore::test(cx);
15751        cx.set_global(store);
15752        theme::init(theme::LoadThemes::JustBase, cx);
15753        release_channel::init(SemanticVersion::default(), cx);
15754        client::init_settings(cx);
15755        language::init(cx);
15756        Project::init_settings(cx);
15757        workspace::init_settings(cx);
15758        crate::init(cx);
15759    });
15760
15761    update_test_language_settings(cx, f);
15762}
15763
15764#[track_caller]
15765fn assert_hunk_revert(
15766    not_reverted_text_with_selections: &str,
15767    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15768    expected_reverted_text_with_selections: &str,
15769    base_text: &str,
15770    cx: &mut EditorLspTestContext,
15771) {
15772    cx.set_state(not_reverted_text_with_selections);
15773    cx.set_diff_base(base_text);
15774    cx.executor().run_until_parked();
15775
15776    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15777        let snapshot = editor.snapshot(window, cx);
15778        let reverted_hunk_statuses = snapshot
15779            .buffer_snapshot
15780            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15781            .map(|hunk| hunk.status())
15782            .collect::<Vec<_>>();
15783
15784        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15785        reverted_hunk_statuses
15786    });
15787    cx.executor().run_until_parked();
15788    cx.assert_editor_state(expected_reverted_text_with_selections);
15789    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15790}