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 model = cx.entity().clone();
   66            cx.subscribe_in(
   67                &model,
   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 unstaged comment
 5623
 5624            fn b() {
 5625                c();
 5626            }
 5627
 5628            // this is another unstaged 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 unstaged comment
 5672
 5673            fn b() {
 5674                c();
 5675            }
 5676
 5677      -     // this is another unstaged 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 unstaged comment
 5695
 5696            fn b() {
 5697 5698            }
 5699
 5700            // this is another unstaged 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_model = leader.root(cx).unwrap();
10200    let follower_model = 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_model,
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_model,
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_model = workspace.root(cx).unwrap();
10388    let follower_1 = cx
10389        .update_window(*workspace.deref(), |_, window, cx| {
10390            Editor::from_state_proto(
10391                workspace_model,
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_model = workspace.root(cx).unwrap();
10490    let follower_2 = cx
10491        .update_window(*workspace.deref(), |_, window, cx| {
10492            Editor::from_state_proto(
10493                workspace_model,
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 = cx
12435                .new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &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 = cx.new(|cx| {
13129                    BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
13130                });
13131                editor
13132                    .buffer
13133                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13134            }
13135        })
13136        .unwrap();
13137
13138    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13139    cx.run_until_parked();
13140
13141    cx.assert_editor_state(
13142        &"
13143            ˇaaa
13144            ccc
13145            ddd
13146
13147            ggg
13148            hhh
13149
13150
13151            lll
13152            mmm
13153            NNN
13154
13155            qqq
13156            rrr
13157
13158            uuu
13159            111
13160            222
13161            333
13162
13163            666
13164            777
13165
13166            000
13167            !!!"
13168        .unindent(),
13169    );
13170
13171    cx.update_editor(|editor, window, cx| {
13172        editor.select_all(&SelectAll, window, cx);
13173        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13174    });
13175    cx.executor().run_until_parked();
13176
13177    cx.assert_state_with_diff(
13178        "
13179            «aaa
13180          - bbb
13181            ccc
13182            ddd
13183
13184            ggg
13185            hhh
13186
13187
13188            lll
13189            mmm
13190          - nnn
13191          + NNN
13192
13193            qqq
13194            rrr
13195
13196            uuu
13197            111
13198            222
13199            333
13200
13201          + 666
13202            777
13203
13204            000
13205            !!!ˇ»"
13206            .unindent(),
13207    );
13208}
13209
13210#[gpui::test]
13211async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13212    init_test(cx, |_| {});
13213
13214    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13215    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
13216
13217    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13218    let multi_buffer = cx.new(|cx| {
13219        let mut multibuffer = MultiBuffer::new(ReadWrite);
13220        multibuffer.push_excerpts(
13221            buffer.clone(),
13222            [
13223                ExcerptRange {
13224                    context: Point::new(0, 0)..Point::new(2, 0),
13225                    primary: None,
13226                },
13227                ExcerptRange {
13228                    context: Point::new(5, 0)..Point::new(7, 0),
13229                    primary: None,
13230                },
13231            ],
13232            cx,
13233        );
13234        multibuffer
13235    });
13236
13237    let editor = cx.add_window(|window, cx| {
13238        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13239    });
13240    editor
13241        .update(cx, |editor, _window, cx| {
13242            let change_set =
13243                cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
13244            editor
13245                .buffer
13246                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13247        })
13248        .unwrap();
13249
13250    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13251    cx.run_until_parked();
13252
13253    cx.update_editor(|editor, window, cx| {
13254        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13255    });
13256    cx.executor().run_until_parked();
13257
13258    cx.assert_state_with_diff(
13259        "
13260            ˇaaa
13261          - bbb
13262          + BBB
13263
13264          + EEE
13265            fff
13266        "
13267        .unindent(),
13268    );
13269}
13270
13271#[gpui::test]
13272async fn test_edits_around_expanded_insertion_hunks(
13273    executor: BackgroundExecutor,
13274    cx: &mut gpui::TestAppContext,
13275) {
13276    init_test(cx, |_| {});
13277
13278    let mut cx = EditorTestContext::new(cx).await;
13279
13280    let diff_base = r#"
13281        use some::mod1;
13282        use some::mod2;
13283
13284        const A: u32 = 42;
13285
13286        fn main() {
13287            println!("hello");
13288
13289            println!("world");
13290        }
13291        "#
13292    .unindent();
13293    executor.run_until_parked();
13294    cx.set_state(
13295        &r#"
13296        use some::mod1;
13297        use some::mod2;
13298
13299        const A: u32 = 42;
13300        const B: u32 = 42;
13301        const C: u32 = 42;
13302        ˇ
13303
13304        fn main() {
13305            println!("hello");
13306
13307            println!("world");
13308        }
13309        "#
13310        .unindent(),
13311    );
13312
13313    cx.set_diff_base(&diff_base);
13314    executor.run_until_parked();
13315
13316    cx.update_editor(|editor, window, cx| {
13317        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13318    });
13319    executor.run_until_parked();
13320
13321    cx.assert_state_with_diff(
13322        r#"
13323        use some::mod1;
13324        use some::mod2;
13325
13326        const A: u32 = 42;
13327      + const B: u32 = 42;
13328      + const C: u32 = 42;
13329      + ˇ
13330
13331        fn main() {
13332            println!("hello");
13333
13334            println!("world");
13335        }
13336      "#
13337        .unindent(),
13338    );
13339
13340    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13341    executor.run_until_parked();
13342
13343    cx.assert_state_with_diff(
13344        r#"
13345        use some::mod1;
13346        use some::mod2;
13347
13348        const A: u32 = 42;
13349      + const B: u32 = 42;
13350      + const C: u32 = 42;
13351      + const D: u32 = 42;
13352      + ˇ
13353
13354        fn main() {
13355            println!("hello");
13356
13357            println!("world");
13358        }
13359      "#
13360        .unindent(),
13361    );
13362
13363    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13364    executor.run_until_parked();
13365
13366    cx.assert_state_with_diff(
13367        r#"
13368        use some::mod1;
13369        use some::mod2;
13370
13371        const A: u32 = 42;
13372      + const B: u32 = 42;
13373      + const C: u32 = 42;
13374      + const D: u32 = 42;
13375      + const E: u32 = 42;
13376      + ˇ
13377
13378        fn main() {
13379            println!("hello");
13380
13381            println!("world");
13382        }
13383      "#
13384        .unindent(),
13385    );
13386
13387    cx.update_editor(|editor, window, cx| {
13388        editor.delete_line(&DeleteLine, window, cx);
13389    });
13390    executor.run_until_parked();
13391
13392    cx.assert_state_with_diff(
13393        r#"
13394        use some::mod1;
13395        use some::mod2;
13396
13397        const A: u32 = 42;
13398      + const B: u32 = 42;
13399      + const C: u32 = 42;
13400      + const D: u32 = 42;
13401      + const E: u32 = 42;
13402        ˇ
13403        fn main() {
13404            println!("hello");
13405
13406            println!("world");
13407        }
13408      "#
13409        .unindent(),
13410    );
13411
13412    cx.update_editor(|editor, window, cx| {
13413        editor.move_up(&MoveUp, window, cx);
13414        editor.delete_line(&DeleteLine, window, cx);
13415        editor.move_up(&MoveUp, window, cx);
13416        editor.delete_line(&DeleteLine, window, cx);
13417        editor.move_up(&MoveUp, window, cx);
13418        editor.delete_line(&DeleteLine, window, cx);
13419    });
13420    executor.run_until_parked();
13421    cx.assert_state_with_diff(
13422        r#"
13423        use some::mod1;
13424        use some::mod2;
13425
13426        const A: u32 = 42;
13427      + const B: u32 = 42;
13428        ˇ
13429        fn main() {
13430            println!("hello");
13431
13432            println!("world");
13433        }
13434      "#
13435        .unindent(),
13436    );
13437
13438    cx.update_editor(|editor, window, cx| {
13439        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13440        editor.delete_line(&DeleteLine, window, cx);
13441    });
13442    executor.run_until_parked();
13443    cx.assert_state_with_diff(
13444        r#"
13445        ˇ
13446        fn main() {
13447            println!("hello");
13448
13449            println!("world");
13450        }
13451      "#
13452        .unindent(),
13453    );
13454}
13455
13456#[gpui::test]
13457async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13458    init_test(cx, |_| {});
13459
13460    let mut cx = EditorTestContext::new(cx).await;
13461    cx.set_diff_base(indoc! { "
13462        one
13463        two
13464        three
13465        four
13466        five
13467        "
13468    });
13469    cx.set_state(indoc! { "
13470        one
13471        ˇthree
13472        five
13473    "});
13474    cx.run_until_parked();
13475    cx.update_editor(|editor, window, cx| {
13476        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13477    });
13478    cx.assert_state_with_diff(
13479        indoc! { "
13480        one
13481      - two
13482        ˇthree
13483      - four
13484        five
13485    "}
13486        .to_string(),
13487    );
13488    cx.update_editor(|editor, window, cx| {
13489        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13490    });
13491
13492    cx.assert_state_with_diff(
13493        indoc! { "
13494        one
13495        ˇthree
13496        five
13497    "}
13498        .to_string(),
13499    );
13500
13501    cx.set_state(indoc! { "
13502        one
13503        TWO
13504        ˇthree
13505        four
13506        five
13507    "});
13508    cx.run_until_parked();
13509    cx.update_editor(|editor, window, cx| {
13510        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13511    });
13512
13513    cx.assert_state_with_diff(
13514        indoc! { "
13515            one
13516          - two
13517          + TWO
13518            ˇthree
13519            four
13520            five
13521        "}
13522        .to_string(),
13523    );
13524    cx.update_editor(|editor, window, cx| {
13525        editor.move_up(&Default::default(), window, cx);
13526        editor.move_up(&Default::default(), window, cx);
13527        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13528    });
13529    cx.assert_state_with_diff(
13530        indoc! { "
13531            one
13532            ˇTWO
13533            three
13534            four
13535            five
13536        "}
13537        .to_string(),
13538    );
13539}
13540
13541#[gpui::test]
13542async fn test_edits_around_expanded_deletion_hunks(
13543    executor: BackgroundExecutor,
13544    cx: &mut gpui::TestAppContext,
13545) {
13546    init_test(cx, |_| {});
13547
13548    let mut cx = EditorTestContext::new(cx).await;
13549
13550    let diff_base = r#"
13551        use some::mod1;
13552        use some::mod2;
13553
13554        const A: u32 = 42;
13555        const B: u32 = 42;
13556        const C: u32 = 42;
13557
13558
13559        fn main() {
13560            println!("hello");
13561
13562            println!("world");
13563        }
13564    "#
13565    .unindent();
13566    executor.run_until_parked();
13567    cx.set_state(
13568        &r#"
13569        use some::mod1;
13570        use some::mod2;
13571
13572        ˇconst B: u32 = 42;
13573        const C: u32 = 42;
13574
13575
13576        fn main() {
13577            println!("hello");
13578
13579            println!("world");
13580        }
13581        "#
13582        .unindent(),
13583    );
13584
13585    cx.set_diff_base(&diff_base);
13586    executor.run_until_parked();
13587
13588    cx.update_editor(|editor, window, cx| {
13589        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13590    });
13591    executor.run_until_parked();
13592
13593    cx.assert_state_with_diff(
13594        r#"
13595        use some::mod1;
13596        use some::mod2;
13597
13598      - const A: u32 = 42;
13599        ˇconst B: u32 = 42;
13600        const C: u32 = 42;
13601
13602
13603        fn main() {
13604            println!("hello");
13605
13606            println!("world");
13607        }
13608      "#
13609        .unindent(),
13610    );
13611
13612    cx.update_editor(|editor, window, cx| {
13613        editor.delete_line(&DeleteLine, window, cx);
13614    });
13615    executor.run_until_parked();
13616    cx.assert_state_with_diff(
13617        r#"
13618        use some::mod1;
13619        use some::mod2;
13620
13621      - const A: u32 = 42;
13622      - const B: u32 = 42;
13623        ˇconst C: u32 = 42;
13624
13625
13626        fn main() {
13627            println!("hello");
13628
13629            println!("world");
13630        }
13631      "#
13632        .unindent(),
13633    );
13634
13635    cx.update_editor(|editor, window, cx| {
13636        editor.delete_line(&DeleteLine, window, cx);
13637    });
13638    executor.run_until_parked();
13639    cx.assert_state_with_diff(
13640        r#"
13641        use some::mod1;
13642        use some::mod2;
13643
13644      - const A: u32 = 42;
13645      - const B: u32 = 42;
13646      - const C: u32 = 42;
13647        ˇ
13648
13649        fn main() {
13650            println!("hello");
13651
13652            println!("world");
13653        }
13654      "#
13655        .unindent(),
13656    );
13657
13658    cx.update_editor(|editor, window, cx| {
13659        editor.handle_input("replacement", window, cx);
13660    });
13661    executor.run_until_parked();
13662    cx.assert_state_with_diff(
13663        r#"
13664        use some::mod1;
13665        use some::mod2;
13666
13667      - const A: u32 = 42;
13668      - const B: u32 = 42;
13669      - const C: u32 = 42;
13670      -
13671      + replacementˇ
13672
13673        fn main() {
13674            println!("hello");
13675
13676            println!("world");
13677        }
13678      "#
13679        .unindent(),
13680    );
13681}
13682
13683#[gpui::test]
13684async fn test_backspace_after_deletion_hunk(
13685    executor: BackgroundExecutor,
13686    cx: &mut gpui::TestAppContext,
13687) {
13688    init_test(cx, |_| {});
13689
13690    let mut cx = EditorTestContext::new(cx).await;
13691
13692    let base_text = r#"
13693        one
13694        two
13695        three
13696        four
13697        five
13698    "#
13699    .unindent();
13700    executor.run_until_parked();
13701    cx.set_state(
13702        &r#"
13703        one
13704        two
13705        fˇour
13706        five
13707        "#
13708        .unindent(),
13709    );
13710
13711    cx.set_diff_base(&base_text);
13712    executor.run_until_parked();
13713
13714    cx.update_editor(|editor, window, cx| {
13715        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13716    });
13717    executor.run_until_parked();
13718
13719    cx.assert_state_with_diff(
13720        r#"
13721          one
13722          two
13723        - three
13724          fˇour
13725          five
13726        "#
13727        .unindent(),
13728    );
13729
13730    cx.update_editor(|editor, window, cx| {
13731        editor.backspace(&Backspace, window, cx);
13732        editor.backspace(&Backspace, window, cx);
13733    });
13734    executor.run_until_parked();
13735    cx.assert_state_with_diff(
13736        r#"
13737          one
13738          two
13739        - threeˇ
13740        - four
13741        + our
13742          five
13743        "#
13744        .unindent(),
13745    );
13746}
13747
13748#[gpui::test]
13749async fn test_edit_after_expanded_modification_hunk(
13750    executor: BackgroundExecutor,
13751    cx: &mut gpui::TestAppContext,
13752) {
13753    init_test(cx, |_| {});
13754
13755    let mut cx = EditorTestContext::new(cx).await;
13756
13757    let diff_base = r#"
13758        use some::mod1;
13759        use some::mod2;
13760
13761        const A: u32 = 42;
13762        const B: u32 = 42;
13763        const C: u32 = 42;
13764        const D: u32 = 42;
13765
13766
13767        fn main() {
13768            println!("hello");
13769
13770            println!("world");
13771        }"#
13772    .unindent();
13773
13774    cx.set_state(
13775        &r#"
13776        use some::mod1;
13777        use some::mod2;
13778
13779        const A: u32 = 42;
13780        const B: u32 = 42;
13781        const C: u32 = 43ˇ
13782        const D: u32 = 42;
13783
13784
13785        fn main() {
13786            println!("hello");
13787
13788            println!("world");
13789        }"#
13790        .unindent(),
13791    );
13792
13793    cx.set_diff_base(&diff_base);
13794    executor.run_until_parked();
13795    cx.update_editor(|editor, window, cx| {
13796        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13797    });
13798    executor.run_until_parked();
13799
13800    cx.assert_state_with_diff(
13801        r#"
13802        use some::mod1;
13803        use some::mod2;
13804
13805        const A: u32 = 42;
13806        const B: u32 = 42;
13807      - const C: u32 = 42;
13808      + const C: u32 = 43ˇ
13809        const D: u32 = 42;
13810
13811
13812        fn main() {
13813            println!("hello");
13814
13815            println!("world");
13816        }"#
13817        .unindent(),
13818    );
13819
13820    cx.update_editor(|editor, window, cx| {
13821        editor.handle_input("\nnew_line\n", window, cx);
13822    });
13823    executor.run_until_parked();
13824
13825    cx.assert_state_with_diff(
13826        r#"
13827        use some::mod1;
13828        use some::mod2;
13829
13830        const A: u32 = 42;
13831        const B: u32 = 42;
13832      - const C: u32 = 42;
13833      + const C: u32 = 43
13834      + new_line
13835      + ˇ
13836        const D: u32 = 42;
13837
13838
13839        fn main() {
13840            println!("hello");
13841
13842            println!("world");
13843        }"#
13844        .unindent(),
13845    );
13846}
13847
13848async fn setup_indent_guides_editor(
13849    text: &str,
13850    cx: &mut gpui::TestAppContext,
13851) -> (BufferId, EditorTestContext) {
13852    init_test(cx, |_| {});
13853
13854    let mut cx = EditorTestContext::new(cx).await;
13855
13856    let buffer_id = cx.update_editor(|editor, window, cx| {
13857        editor.set_text(text, window, cx);
13858        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13859
13860        buffer_ids[0]
13861    });
13862
13863    (buffer_id, cx)
13864}
13865
13866fn assert_indent_guides(
13867    range: Range<u32>,
13868    expected: Vec<IndentGuide>,
13869    active_indices: Option<Vec<usize>>,
13870    cx: &mut EditorTestContext,
13871) {
13872    let indent_guides = cx.update_editor(|editor, window, cx| {
13873        let snapshot = editor.snapshot(window, cx).display_snapshot;
13874        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13875            editor,
13876            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13877            true,
13878            &snapshot,
13879            cx,
13880        );
13881
13882        indent_guides.sort_by(|a, b| {
13883            a.depth.cmp(&b.depth).then(
13884                a.start_row
13885                    .cmp(&b.start_row)
13886                    .then(a.end_row.cmp(&b.end_row)),
13887            )
13888        });
13889        indent_guides
13890    });
13891
13892    if let Some(expected) = active_indices {
13893        let active_indices = cx.update_editor(|editor, window, cx| {
13894            let snapshot = editor.snapshot(window, cx).display_snapshot;
13895            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13896        });
13897
13898        assert_eq!(
13899            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13900            expected,
13901            "Active indent guide indices do not match"
13902        );
13903    }
13904
13905    assert_eq!(indent_guides, expected, "Indent guides do not match");
13906}
13907
13908fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13909    IndentGuide {
13910        buffer_id,
13911        start_row: MultiBufferRow(start_row),
13912        end_row: MultiBufferRow(end_row),
13913        depth,
13914        tab_size: 4,
13915        settings: IndentGuideSettings {
13916            enabled: true,
13917            line_width: 1,
13918            active_line_width: 1,
13919            ..Default::default()
13920        },
13921    }
13922}
13923
13924#[gpui::test]
13925async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13926    let (buffer_id, mut cx) = setup_indent_guides_editor(
13927        &"
13928    fn main() {
13929        let a = 1;
13930    }"
13931        .unindent(),
13932        cx,
13933    )
13934    .await;
13935
13936    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13937}
13938
13939#[gpui::test]
13940async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13941    let (buffer_id, mut cx) = setup_indent_guides_editor(
13942        &"
13943    fn main() {
13944        let a = 1;
13945        let b = 2;
13946    }"
13947        .unindent(),
13948        cx,
13949    )
13950    .await;
13951
13952    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13953}
13954
13955#[gpui::test]
13956async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13957    let (buffer_id, mut cx) = setup_indent_guides_editor(
13958        &"
13959    fn main() {
13960        let a = 1;
13961        if a == 3 {
13962            let b = 2;
13963        } else {
13964            let c = 3;
13965        }
13966    }"
13967        .unindent(),
13968        cx,
13969    )
13970    .await;
13971
13972    assert_indent_guides(
13973        0..8,
13974        vec![
13975            indent_guide(buffer_id, 1, 6, 0),
13976            indent_guide(buffer_id, 3, 3, 1),
13977            indent_guide(buffer_id, 5, 5, 1),
13978        ],
13979        None,
13980        &mut cx,
13981    );
13982}
13983
13984#[gpui::test]
13985async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13986    let (buffer_id, mut cx) = setup_indent_guides_editor(
13987        &"
13988    fn main() {
13989        let a = 1;
13990            let b = 2;
13991        let c = 3;
13992    }"
13993        .unindent(),
13994        cx,
13995    )
13996    .await;
13997
13998    assert_indent_guides(
13999        0..5,
14000        vec![
14001            indent_guide(buffer_id, 1, 3, 0),
14002            indent_guide(buffer_id, 2, 2, 1),
14003        ],
14004        None,
14005        &mut cx,
14006    );
14007}
14008
14009#[gpui::test]
14010async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14011    let (buffer_id, mut cx) = setup_indent_guides_editor(
14012        &"
14013        fn main() {
14014            let a = 1;
14015
14016            let c = 3;
14017        }"
14018        .unindent(),
14019        cx,
14020    )
14021    .await;
14022
14023    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14024}
14025
14026#[gpui::test]
14027async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14028    let (buffer_id, mut cx) = setup_indent_guides_editor(
14029        &"
14030        fn main() {
14031            let a = 1;
14032
14033            let c = 3;
14034
14035            if a == 3 {
14036                let b = 2;
14037            } else {
14038                let c = 3;
14039            }
14040        }"
14041        .unindent(),
14042        cx,
14043    )
14044    .await;
14045
14046    assert_indent_guides(
14047        0..11,
14048        vec![
14049            indent_guide(buffer_id, 1, 9, 0),
14050            indent_guide(buffer_id, 6, 6, 1),
14051            indent_guide(buffer_id, 8, 8, 1),
14052        ],
14053        None,
14054        &mut cx,
14055    );
14056}
14057
14058#[gpui::test]
14059async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14060    let (buffer_id, mut cx) = setup_indent_guides_editor(
14061        &"
14062        fn main() {
14063            let a = 1;
14064
14065            let c = 3;
14066
14067            if a == 3 {
14068                let b = 2;
14069            } else {
14070                let c = 3;
14071            }
14072        }"
14073        .unindent(),
14074        cx,
14075    )
14076    .await;
14077
14078    assert_indent_guides(
14079        1..11,
14080        vec![
14081            indent_guide(buffer_id, 1, 9, 0),
14082            indent_guide(buffer_id, 6, 6, 1),
14083            indent_guide(buffer_id, 8, 8, 1),
14084        ],
14085        None,
14086        &mut cx,
14087    );
14088}
14089
14090#[gpui::test]
14091async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14092    let (buffer_id, mut cx) = setup_indent_guides_editor(
14093        &"
14094        fn main() {
14095            let a = 1;
14096
14097            let c = 3;
14098
14099            if a == 3 {
14100                let b = 2;
14101            } else {
14102                let c = 3;
14103            }
14104        }"
14105        .unindent(),
14106        cx,
14107    )
14108    .await;
14109
14110    assert_indent_guides(
14111        1..10,
14112        vec![
14113            indent_guide(buffer_id, 1, 9, 0),
14114            indent_guide(buffer_id, 6, 6, 1),
14115            indent_guide(buffer_id, 8, 8, 1),
14116        ],
14117        None,
14118        &mut cx,
14119    );
14120}
14121
14122#[gpui::test]
14123async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14124    let (buffer_id, mut cx) = setup_indent_guides_editor(
14125        &"
14126        block1
14127            block2
14128                block3
14129                    block4
14130            block2
14131        block1
14132        block1"
14133            .unindent(),
14134        cx,
14135    )
14136    .await;
14137
14138    assert_indent_guides(
14139        1..10,
14140        vec![
14141            indent_guide(buffer_id, 1, 4, 0),
14142            indent_guide(buffer_id, 2, 3, 1),
14143            indent_guide(buffer_id, 3, 3, 2),
14144        ],
14145        None,
14146        &mut cx,
14147    );
14148}
14149
14150#[gpui::test]
14151async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14152    let (buffer_id, mut cx) = setup_indent_guides_editor(
14153        &"
14154        block1
14155            block2
14156                block3
14157
14158        block1
14159        block1"
14160            .unindent(),
14161        cx,
14162    )
14163    .await;
14164
14165    assert_indent_guides(
14166        0..6,
14167        vec![
14168            indent_guide(buffer_id, 1, 2, 0),
14169            indent_guide(buffer_id, 2, 2, 1),
14170        ],
14171        None,
14172        &mut cx,
14173    );
14174}
14175
14176#[gpui::test]
14177async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14178    let (buffer_id, mut cx) = setup_indent_guides_editor(
14179        &"
14180        block1
14181
14182
14183
14184            block2
14185        "
14186        .unindent(),
14187        cx,
14188    )
14189    .await;
14190
14191    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14192}
14193
14194#[gpui::test]
14195async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14196    let (buffer_id, mut cx) = setup_indent_guides_editor(
14197        &"
14198        def a:
14199        \tb = 3
14200        \tif True:
14201        \t\tc = 4
14202        \t\td = 5
14203        \tprint(b)
14204        "
14205        .unindent(),
14206        cx,
14207    )
14208    .await;
14209
14210    assert_indent_guides(
14211        0..6,
14212        vec![
14213            indent_guide(buffer_id, 1, 6, 0),
14214            indent_guide(buffer_id, 3, 4, 1),
14215        ],
14216        None,
14217        &mut cx,
14218    );
14219}
14220
14221#[gpui::test]
14222async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14223    let (buffer_id, mut cx) = setup_indent_guides_editor(
14224        &"
14225    fn main() {
14226        let a = 1;
14227    }"
14228        .unindent(),
14229        cx,
14230    )
14231    .await;
14232
14233    cx.update_editor(|editor, window, cx| {
14234        editor.change_selections(None, window, cx, |s| {
14235            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14236        });
14237    });
14238
14239    assert_indent_guides(
14240        0..3,
14241        vec![indent_guide(buffer_id, 1, 1, 0)],
14242        Some(vec![0]),
14243        &mut cx,
14244    );
14245}
14246
14247#[gpui::test]
14248async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14249    let (buffer_id, mut cx) = setup_indent_guides_editor(
14250        &"
14251    fn main() {
14252        if 1 == 2 {
14253            let a = 1;
14254        }
14255    }"
14256        .unindent(),
14257        cx,
14258    )
14259    .await;
14260
14261    cx.update_editor(|editor, window, cx| {
14262        editor.change_selections(None, window, cx, |s| {
14263            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14264        });
14265    });
14266
14267    assert_indent_guides(
14268        0..4,
14269        vec![
14270            indent_guide(buffer_id, 1, 3, 0),
14271            indent_guide(buffer_id, 2, 2, 1),
14272        ],
14273        Some(vec![1]),
14274        &mut cx,
14275    );
14276
14277    cx.update_editor(|editor, window, cx| {
14278        editor.change_selections(None, window, cx, |s| {
14279            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14280        });
14281    });
14282
14283    assert_indent_guides(
14284        0..4,
14285        vec![
14286            indent_guide(buffer_id, 1, 3, 0),
14287            indent_guide(buffer_id, 2, 2, 1),
14288        ],
14289        Some(vec![1]),
14290        &mut cx,
14291    );
14292
14293    cx.update_editor(|editor, window, cx| {
14294        editor.change_selections(None, window, cx, |s| {
14295            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14296        });
14297    });
14298
14299    assert_indent_guides(
14300        0..4,
14301        vec![
14302            indent_guide(buffer_id, 1, 3, 0),
14303            indent_guide(buffer_id, 2, 2, 1),
14304        ],
14305        Some(vec![0]),
14306        &mut cx,
14307    );
14308}
14309
14310#[gpui::test]
14311async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14312    let (buffer_id, mut cx) = setup_indent_guides_editor(
14313        &"
14314    fn main() {
14315        let a = 1;
14316
14317        let b = 2;
14318    }"
14319        .unindent(),
14320        cx,
14321    )
14322    .await;
14323
14324    cx.update_editor(|editor, window, cx| {
14325        editor.change_selections(None, window, cx, |s| {
14326            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14327        });
14328    });
14329
14330    assert_indent_guides(
14331        0..5,
14332        vec![indent_guide(buffer_id, 1, 3, 0)],
14333        Some(vec![0]),
14334        &mut cx,
14335    );
14336}
14337
14338#[gpui::test]
14339async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14340    let (buffer_id, mut cx) = setup_indent_guides_editor(
14341        &"
14342    def m:
14343        a = 1
14344        pass"
14345            .unindent(),
14346        cx,
14347    )
14348    .await;
14349
14350    cx.update_editor(|editor, window, cx| {
14351        editor.change_selections(None, window, cx, |s| {
14352            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14353        });
14354    });
14355
14356    assert_indent_guides(
14357        0..3,
14358        vec![indent_guide(buffer_id, 1, 2, 0)],
14359        Some(vec![0]),
14360        &mut cx,
14361    );
14362}
14363
14364#[gpui::test]
14365async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14366    init_test(cx, |_| {});
14367    let mut cx = EditorTestContext::new(cx).await;
14368    let text = indoc! {
14369        "
14370        impl A {
14371            fn b() {
14372                0;
14373                3;
14374                5;
14375                6;
14376                7;
14377            }
14378        }
14379        "
14380    };
14381    let base_text = indoc! {
14382        "
14383        impl A {
14384            fn b() {
14385                0;
14386                1;
14387                2;
14388                3;
14389                4;
14390            }
14391            fn c() {
14392                5;
14393                6;
14394                7;
14395            }
14396        }
14397        "
14398    };
14399
14400    cx.update_editor(|editor, window, cx| {
14401        editor.set_text(text, window, cx);
14402
14403        editor.buffer().update(cx, |multibuffer, cx| {
14404            let buffer = multibuffer.as_singleton().unwrap();
14405            let change_set = cx.new(|cx| {
14406                let mut change_set = BufferChangeSet::new(&buffer, cx);
14407                let _ =
14408                    change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
14409                change_set
14410            });
14411
14412            multibuffer.set_all_diff_hunks_expanded(cx);
14413            multibuffer.add_change_set(change_set, cx);
14414
14415            buffer.read(cx).remote_id()
14416        })
14417    });
14418    cx.run_until_parked();
14419
14420    cx.assert_state_with_diff(
14421        indoc! { "
14422          impl A {
14423              fn b() {
14424                  0;
14425        -         1;
14426        -         2;
14427                  3;
14428        -         4;
14429        -     }
14430        -     fn c() {
14431                  5;
14432                  6;
14433                  7;
14434              }
14435          }
14436          ˇ"
14437        }
14438        .to_string(),
14439    );
14440
14441    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14442        editor
14443            .snapshot(window, cx)
14444            .buffer_snapshot
14445            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14446            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14447            .collect::<Vec<_>>()
14448    });
14449    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14450    assert_eq!(
14451        actual_guides,
14452        vec![
14453            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14454            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14455            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14456        ]
14457    );
14458}
14459
14460#[gpui::test]
14461fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14462    init_test(cx, |_| {});
14463
14464    let editor = cx.add_window(|window, cx| {
14465        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14466        build_editor(buffer, window, cx)
14467    });
14468
14469    let render_args = Arc::new(Mutex::new(None));
14470    let snapshot = editor
14471        .update(cx, |editor, window, cx| {
14472            let snapshot = editor.buffer().read(cx).snapshot(cx);
14473            let range =
14474                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14475
14476            struct RenderArgs {
14477                row: MultiBufferRow,
14478                folded: bool,
14479                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14480            }
14481
14482            let crease = Crease::inline(
14483                range,
14484                FoldPlaceholder::test(),
14485                {
14486                    let toggle_callback = render_args.clone();
14487                    move |row, folded, callback, _window, _cx| {
14488                        *toggle_callback.lock() = Some(RenderArgs {
14489                            row,
14490                            folded,
14491                            callback,
14492                        });
14493                        div()
14494                    }
14495                },
14496                |_row, _folded, _window, _cx| div(),
14497            );
14498
14499            editor.insert_creases(Some(crease), cx);
14500            let snapshot = editor.snapshot(window, cx);
14501            let _div = snapshot.render_crease_toggle(
14502                MultiBufferRow(1),
14503                false,
14504                cx.entity().clone(),
14505                window,
14506                cx,
14507            );
14508            snapshot
14509        })
14510        .unwrap();
14511
14512    let render_args = render_args.lock().take().unwrap();
14513    assert_eq!(render_args.row, MultiBufferRow(1));
14514    assert!(!render_args.folded);
14515    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14516
14517    cx.update_window(*editor, |_, window, cx| {
14518        (render_args.callback)(true, window, cx)
14519    })
14520    .unwrap();
14521    let snapshot = editor
14522        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14523        .unwrap();
14524    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14525
14526    cx.update_window(*editor, |_, window, cx| {
14527        (render_args.callback)(false, window, cx)
14528    })
14529    .unwrap();
14530    let snapshot = editor
14531        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14532        .unwrap();
14533    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14534}
14535
14536#[gpui::test]
14537async fn test_input_text(cx: &mut gpui::TestAppContext) {
14538    init_test(cx, |_| {});
14539    let mut cx = EditorTestContext::new(cx).await;
14540
14541    cx.set_state(
14542        &r#"ˇone
14543        two
14544
14545        three
14546        fourˇ
14547        five
14548
14549        siˇx"#
14550            .unindent(),
14551    );
14552
14553    cx.dispatch_action(HandleInput(String::new()));
14554    cx.assert_editor_state(
14555        &r#"ˇone
14556        two
14557
14558        three
14559        fourˇ
14560        five
14561
14562        siˇx"#
14563            .unindent(),
14564    );
14565
14566    cx.dispatch_action(HandleInput("AAAA".to_string()));
14567    cx.assert_editor_state(
14568        &r#"AAAAˇone
14569        two
14570
14571        three
14572        fourAAAAˇ
14573        five
14574
14575        siAAAAˇx"#
14576            .unindent(),
14577    );
14578}
14579
14580#[gpui::test]
14581async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14582    init_test(cx, |_| {});
14583
14584    let mut cx = EditorTestContext::new(cx).await;
14585    cx.set_state(
14586        r#"let foo = 1;
14587let foo = 2;
14588let foo = 3;
14589let fooˇ = 4;
14590let foo = 5;
14591let foo = 6;
14592let foo = 7;
14593let foo = 8;
14594let foo = 9;
14595let foo = 10;
14596let foo = 11;
14597let foo = 12;
14598let foo = 13;
14599let foo = 14;
14600let foo = 15;"#,
14601    );
14602
14603    cx.update_editor(|e, window, cx| {
14604        assert_eq!(
14605            e.next_scroll_position,
14606            NextScrollCursorCenterTopBottom::Center,
14607            "Default next scroll direction is center",
14608        );
14609
14610        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14611        assert_eq!(
14612            e.next_scroll_position,
14613            NextScrollCursorCenterTopBottom::Top,
14614            "After center, next scroll direction should be top",
14615        );
14616
14617        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14618        assert_eq!(
14619            e.next_scroll_position,
14620            NextScrollCursorCenterTopBottom::Bottom,
14621            "After top, next scroll direction should be bottom",
14622        );
14623
14624        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14625        assert_eq!(
14626            e.next_scroll_position,
14627            NextScrollCursorCenterTopBottom::Center,
14628            "After bottom, scrolling should start over",
14629        );
14630
14631        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14632        assert_eq!(
14633            e.next_scroll_position,
14634            NextScrollCursorCenterTopBottom::Top,
14635            "Scrolling continues if retriggered fast enough"
14636        );
14637    });
14638
14639    cx.executor()
14640        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14641    cx.executor().run_until_parked();
14642    cx.update_editor(|e, _, _| {
14643        assert_eq!(
14644            e.next_scroll_position,
14645            NextScrollCursorCenterTopBottom::Center,
14646            "If scrolling is not triggered fast enough, it should reset"
14647        );
14648    });
14649}
14650
14651#[gpui::test]
14652async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14653    init_test(cx, |_| {});
14654    let mut cx = EditorLspTestContext::new_rust(
14655        lsp::ServerCapabilities {
14656            definition_provider: Some(lsp::OneOf::Left(true)),
14657            references_provider: Some(lsp::OneOf::Left(true)),
14658            ..lsp::ServerCapabilities::default()
14659        },
14660        cx,
14661    )
14662    .await;
14663
14664    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14665        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14666            move |params, _| async move {
14667                if empty_go_to_definition {
14668                    Ok(None)
14669                } else {
14670                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14671                        uri: params.text_document_position_params.text_document.uri,
14672                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14673                    })))
14674                }
14675            },
14676        );
14677        let references =
14678            cx.lsp
14679                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14680                    Ok(Some(vec![lsp::Location {
14681                        uri: params.text_document_position.text_document.uri,
14682                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14683                    }]))
14684                });
14685        (go_to_definition, references)
14686    };
14687
14688    cx.set_state(
14689        &r#"fn one() {
14690            let mut a = ˇtwo();
14691        }
14692
14693        fn two() {}"#
14694            .unindent(),
14695    );
14696    set_up_lsp_handlers(false, &mut cx);
14697    let navigated = cx
14698        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14699        .await
14700        .expect("Failed to navigate to definition");
14701    assert_eq!(
14702        navigated,
14703        Navigated::Yes,
14704        "Should have navigated to definition from the GetDefinition response"
14705    );
14706    cx.assert_editor_state(
14707        &r#"fn one() {
14708            let mut a = two();
14709        }
14710
14711        fn «twoˇ»() {}"#
14712            .unindent(),
14713    );
14714
14715    let editors = cx.update_workspace(|workspace, _, cx| {
14716        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14717    });
14718    cx.update_editor(|_, _, test_editor_cx| {
14719        assert_eq!(
14720            editors.len(),
14721            1,
14722            "Initially, only one, test, editor should be open in the workspace"
14723        );
14724        assert_eq!(
14725            test_editor_cx.entity(),
14726            editors.last().expect("Asserted len is 1").clone()
14727        );
14728    });
14729
14730    set_up_lsp_handlers(true, &mut cx);
14731    let navigated = cx
14732        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14733        .await
14734        .expect("Failed to navigate to lookup references");
14735    assert_eq!(
14736        navigated,
14737        Navigated::Yes,
14738        "Should have navigated to references as a fallback after empty GoToDefinition response"
14739    );
14740    // We should not change the selections in the existing file,
14741    // if opening another milti buffer with the references
14742    cx.assert_editor_state(
14743        &r#"fn one() {
14744            let mut a = two();
14745        }
14746
14747        fn «twoˇ»() {}"#
14748            .unindent(),
14749    );
14750    let editors = cx.update_workspace(|workspace, _, cx| {
14751        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14752    });
14753    cx.update_editor(|_, _, test_editor_cx| {
14754        assert_eq!(
14755            editors.len(),
14756            2,
14757            "After falling back to references search, we open a new editor with the results"
14758        );
14759        let references_fallback_text = editors
14760            .into_iter()
14761            .find(|new_editor| *new_editor != test_editor_cx.entity())
14762            .expect("Should have one non-test editor now")
14763            .read(test_editor_cx)
14764            .text(test_editor_cx);
14765        assert_eq!(
14766            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14767            "Should use the range from the references response and not the GoToDefinition one"
14768        );
14769    });
14770}
14771
14772#[gpui::test]
14773async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14774    init_test(cx, |_| {});
14775
14776    let language = Arc::new(Language::new(
14777        LanguageConfig::default(),
14778        Some(tree_sitter_rust::LANGUAGE.into()),
14779    ));
14780
14781    let text = r#"
14782        #[cfg(test)]
14783        mod tests() {
14784            #[test]
14785            fn runnable_1() {
14786                let a = 1;
14787            }
14788
14789            #[test]
14790            fn runnable_2() {
14791                let a = 1;
14792                let b = 2;
14793            }
14794        }
14795    "#
14796    .unindent();
14797
14798    let fs = FakeFs::new(cx.executor());
14799    fs.insert_file("/file.rs", Default::default()).await;
14800
14801    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14802    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14803    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14804    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14805    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14806
14807    let editor = cx.new_window_entity(|window, cx| {
14808        Editor::new(
14809            EditorMode::Full,
14810            multi_buffer,
14811            Some(project.clone()),
14812            true,
14813            window,
14814            cx,
14815        )
14816    });
14817
14818    editor.update_in(cx, |editor, window, cx| {
14819        editor.tasks.insert(
14820            (buffer.read(cx).remote_id(), 3),
14821            RunnableTasks {
14822                templates: vec![],
14823                offset: MultiBufferOffset(43),
14824                column: 0,
14825                extra_variables: HashMap::default(),
14826                context_range: BufferOffset(43)..BufferOffset(85),
14827            },
14828        );
14829        editor.tasks.insert(
14830            (buffer.read(cx).remote_id(), 8),
14831            RunnableTasks {
14832                templates: vec![],
14833                offset: MultiBufferOffset(86),
14834                column: 0,
14835                extra_variables: HashMap::default(),
14836                context_range: BufferOffset(86)..BufferOffset(191),
14837            },
14838        );
14839
14840        // Test finding task when cursor is inside function body
14841        editor.change_selections(None, window, cx, |s| {
14842            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14843        });
14844        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14845        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14846
14847        // Test finding task when cursor is on function name
14848        editor.change_selections(None, window, cx, |s| {
14849            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14850        });
14851        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14852        assert_eq!(row, 8, "Should find task when cursor is on function name");
14853    });
14854}
14855
14856#[gpui::test]
14857async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14858    init_test(cx, |_| {});
14859
14860    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14861    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14862    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14863
14864    let fs = FakeFs::new(cx.executor());
14865    fs.insert_tree(
14866        "/a",
14867        json!({
14868            "first.rs": sample_text_1,
14869            "second.rs": sample_text_2,
14870            "third.rs": sample_text_3,
14871        }),
14872    )
14873    .await;
14874    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14875    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14876    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14877    let worktree = project.update(cx, |project, cx| {
14878        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14879        assert_eq!(worktrees.len(), 1);
14880        worktrees.pop().unwrap()
14881    });
14882    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14883
14884    let buffer_1 = project
14885        .update(cx, |project, cx| {
14886            project.open_buffer((worktree_id, "first.rs"), cx)
14887        })
14888        .await
14889        .unwrap();
14890    let buffer_2 = project
14891        .update(cx, |project, cx| {
14892            project.open_buffer((worktree_id, "second.rs"), cx)
14893        })
14894        .await
14895        .unwrap();
14896    let buffer_3 = project
14897        .update(cx, |project, cx| {
14898            project.open_buffer((worktree_id, "third.rs"), cx)
14899        })
14900        .await
14901        .unwrap();
14902
14903    let multi_buffer = cx.new(|cx| {
14904        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14905        multi_buffer.push_excerpts(
14906            buffer_1.clone(),
14907            [
14908                ExcerptRange {
14909                    context: Point::new(0, 0)..Point::new(3, 0),
14910                    primary: None,
14911                },
14912                ExcerptRange {
14913                    context: Point::new(5, 0)..Point::new(7, 0),
14914                    primary: None,
14915                },
14916                ExcerptRange {
14917                    context: Point::new(9, 0)..Point::new(10, 4),
14918                    primary: None,
14919                },
14920            ],
14921            cx,
14922        );
14923        multi_buffer.push_excerpts(
14924            buffer_2.clone(),
14925            [
14926                ExcerptRange {
14927                    context: Point::new(0, 0)..Point::new(3, 0),
14928                    primary: None,
14929                },
14930                ExcerptRange {
14931                    context: Point::new(5, 0)..Point::new(7, 0),
14932                    primary: None,
14933                },
14934                ExcerptRange {
14935                    context: Point::new(9, 0)..Point::new(10, 4),
14936                    primary: None,
14937                },
14938            ],
14939            cx,
14940        );
14941        multi_buffer.push_excerpts(
14942            buffer_3.clone(),
14943            [
14944                ExcerptRange {
14945                    context: Point::new(0, 0)..Point::new(3, 0),
14946                    primary: None,
14947                },
14948                ExcerptRange {
14949                    context: Point::new(5, 0)..Point::new(7, 0),
14950                    primary: None,
14951                },
14952                ExcerptRange {
14953                    context: Point::new(9, 0)..Point::new(10, 4),
14954                    primary: None,
14955                },
14956            ],
14957            cx,
14958        );
14959        multi_buffer
14960    });
14961    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14962        Editor::new(
14963            EditorMode::Full,
14964            multi_buffer,
14965            Some(project.clone()),
14966            true,
14967            window,
14968            cx,
14969        )
14970    });
14971
14972    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";
14973    assert_eq!(
14974        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14975        full_text,
14976    );
14977
14978    multi_buffer_editor.update(cx, |editor, cx| {
14979        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14980    });
14981    assert_eq!(
14982        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14983        "\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",
14984        "After folding the first buffer, its text should not be displayed"
14985    );
14986
14987    multi_buffer_editor.update(cx, |editor, cx| {
14988        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14989    });
14990    assert_eq!(
14991        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14992        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14993        "After folding the second buffer, its text should not be displayed"
14994    );
14995
14996    multi_buffer_editor.update(cx, |editor, cx| {
14997        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14998    });
14999    assert_eq!(
15000        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15001        "\n\n\n\n\n",
15002        "After folding the third buffer, its text should not be displayed"
15003    );
15004
15005    // Emulate selection inside the fold logic, that should work
15006    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15007        editor
15008            .snapshot(window, cx)
15009            .next_line_boundary(Point::new(0, 4));
15010    });
15011
15012    multi_buffer_editor.update(cx, |editor, cx| {
15013        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15014    });
15015    assert_eq!(
15016        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15017        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15018        "After unfolding the second buffer, its text should be displayed"
15019    );
15020
15021    multi_buffer_editor.update(cx, |editor, cx| {
15022        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15023    });
15024    assert_eq!(
15025        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15026        "\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",
15027        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15028    );
15029
15030    multi_buffer_editor.update(cx, |editor, cx| {
15031        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15032    });
15033    assert_eq!(
15034        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15035        full_text,
15036        "After unfolding the all buffers, all original text should be displayed"
15037    );
15038}
15039
15040#[gpui::test]
15041async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15042    init_test(cx, |_| {});
15043
15044    let sample_text_1 = "1111\n2222\n3333".to_string();
15045    let sample_text_2 = "4444\n5555\n6666".to_string();
15046    let sample_text_3 = "7777\n8888\n9999".to_string();
15047
15048    let fs = FakeFs::new(cx.executor());
15049    fs.insert_tree(
15050        "/a",
15051        json!({
15052            "first.rs": sample_text_1,
15053            "second.rs": sample_text_2,
15054            "third.rs": sample_text_3,
15055        }),
15056    )
15057    .await;
15058    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15059    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15060    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15061    let worktree = project.update(cx, |project, cx| {
15062        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15063        assert_eq!(worktrees.len(), 1);
15064        worktrees.pop().unwrap()
15065    });
15066    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15067
15068    let buffer_1 = project
15069        .update(cx, |project, cx| {
15070            project.open_buffer((worktree_id, "first.rs"), cx)
15071        })
15072        .await
15073        .unwrap();
15074    let buffer_2 = project
15075        .update(cx, |project, cx| {
15076            project.open_buffer((worktree_id, "second.rs"), cx)
15077        })
15078        .await
15079        .unwrap();
15080    let buffer_3 = project
15081        .update(cx, |project, cx| {
15082            project.open_buffer((worktree_id, "third.rs"), cx)
15083        })
15084        .await
15085        .unwrap();
15086
15087    let multi_buffer = cx.new(|cx| {
15088        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15089        multi_buffer.push_excerpts(
15090            buffer_1.clone(),
15091            [ExcerptRange {
15092                context: Point::new(0, 0)..Point::new(3, 0),
15093                primary: None,
15094            }],
15095            cx,
15096        );
15097        multi_buffer.push_excerpts(
15098            buffer_2.clone(),
15099            [ExcerptRange {
15100                context: Point::new(0, 0)..Point::new(3, 0),
15101                primary: None,
15102            }],
15103            cx,
15104        );
15105        multi_buffer.push_excerpts(
15106            buffer_3.clone(),
15107            [ExcerptRange {
15108                context: Point::new(0, 0)..Point::new(3, 0),
15109                primary: None,
15110            }],
15111            cx,
15112        );
15113        multi_buffer
15114    });
15115
15116    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15117        Editor::new(
15118            EditorMode::Full,
15119            multi_buffer,
15120            Some(project.clone()),
15121            true,
15122            window,
15123            cx,
15124        )
15125    });
15126
15127    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15128    assert_eq!(
15129        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15130        full_text,
15131    );
15132
15133    multi_buffer_editor.update(cx, |editor, cx| {
15134        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15135    });
15136    assert_eq!(
15137        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15138        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15139        "After folding the first buffer, its text should not be displayed"
15140    );
15141
15142    multi_buffer_editor.update(cx, |editor, cx| {
15143        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15144    });
15145
15146    assert_eq!(
15147        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15148        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15149        "After folding the second buffer, its text should not be displayed"
15150    );
15151
15152    multi_buffer_editor.update(cx, |editor, cx| {
15153        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15154    });
15155    assert_eq!(
15156        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15157        "\n\n\n\n\n",
15158        "After folding the third buffer, its text should not be displayed"
15159    );
15160
15161    multi_buffer_editor.update(cx, |editor, cx| {
15162        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15163    });
15164    assert_eq!(
15165        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15166        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15167        "After unfolding the second buffer, its text should be displayed"
15168    );
15169
15170    multi_buffer_editor.update(cx, |editor, cx| {
15171        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15172    });
15173    assert_eq!(
15174        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15175        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15176        "After unfolding the first buffer, its text should be displayed"
15177    );
15178
15179    multi_buffer_editor.update(cx, |editor, cx| {
15180        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15181    });
15182    assert_eq!(
15183        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15184        full_text,
15185        "After unfolding all buffers, all original text should be displayed"
15186    );
15187}
15188
15189#[gpui::test]
15190async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15191    init_test(cx, |_| {});
15192
15193    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15194
15195    let fs = FakeFs::new(cx.executor());
15196    fs.insert_tree(
15197        "/a",
15198        json!({
15199            "main.rs": sample_text,
15200        }),
15201    )
15202    .await;
15203    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15204    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15205    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15206    let worktree = project.update(cx, |project, cx| {
15207        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15208        assert_eq!(worktrees.len(), 1);
15209        worktrees.pop().unwrap()
15210    });
15211    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15212
15213    let buffer_1 = project
15214        .update(cx, |project, cx| {
15215            project.open_buffer((worktree_id, "main.rs"), cx)
15216        })
15217        .await
15218        .unwrap();
15219
15220    let multi_buffer = cx.new(|cx| {
15221        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15222        multi_buffer.push_excerpts(
15223            buffer_1.clone(),
15224            [ExcerptRange {
15225                context: Point::new(0, 0)
15226                    ..Point::new(
15227                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15228                        0,
15229                    ),
15230                primary: None,
15231            }],
15232            cx,
15233        );
15234        multi_buffer
15235    });
15236    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15237        Editor::new(
15238            EditorMode::Full,
15239            multi_buffer,
15240            Some(project.clone()),
15241            true,
15242            window,
15243            cx,
15244        )
15245    });
15246
15247    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15248    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15249        enum TestHighlight {}
15250        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15251        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15252        editor.highlight_text::<TestHighlight>(
15253            vec![highlight_range.clone()],
15254            HighlightStyle::color(Hsla::green()),
15255            cx,
15256        );
15257        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15258    });
15259
15260    let full_text = format!("\n\n\n{sample_text}\n");
15261    assert_eq!(
15262        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15263        full_text,
15264    );
15265}
15266
15267#[gpui::test]
15268async fn test_inline_completion_text(cx: &mut TestAppContext) {
15269    init_test(cx, |_| {});
15270
15271    // Simple insertion
15272    assert_highlighted_edits(
15273        "Hello, world!",
15274        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15275        true,
15276        cx,
15277        |highlighted_edits, cx| {
15278            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15279            assert_eq!(highlighted_edits.highlights.len(), 1);
15280            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15281            assert_eq!(
15282                highlighted_edits.highlights[0].1.background_color,
15283                Some(cx.theme().status().created_background)
15284            );
15285        },
15286    )
15287    .await;
15288
15289    // Replacement
15290    assert_highlighted_edits(
15291        "This is a test.",
15292        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15293        false,
15294        cx,
15295        |highlighted_edits, cx| {
15296            assert_eq!(highlighted_edits.text, "That is a test.");
15297            assert_eq!(highlighted_edits.highlights.len(), 1);
15298            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15299            assert_eq!(
15300                highlighted_edits.highlights[0].1.background_color,
15301                Some(cx.theme().status().created_background)
15302            );
15303        },
15304    )
15305    .await;
15306
15307    // Multiple edits
15308    assert_highlighted_edits(
15309        "Hello, world!",
15310        vec![
15311            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15312            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15313        ],
15314        false,
15315        cx,
15316        |highlighted_edits, cx| {
15317            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15318            assert_eq!(highlighted_edits.highlights.len(), 2);
15319            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15320            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15321            assert_eq!(
15322                highlighted_edits.highlights[0].1.background_color,
15323                Some(cx.theme().status().created_background)
15324            );
15325            assert_eq!(
15326                highlighted_edits.highlights[1].1.background_color,
15327                Some(cx.theme().status().created_background)
15328            );
15329        },
15330    )
15331    .await;
15332
15333    // Multiple lines with edits
15334    assert_highlighted_edits(
15335        "First line\nSecond line\nThird line\nFourth line",
15336        vec![
15337            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15338            (
15339                Point::new(2, 0)..Point::new(2, 10),
15340                "New third line".to_string(),
15341            ),
15342            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15343        ],
15344        false,
15345        cx,
15346        |highlighted_edits, cx| {
15347            assert_eq!(
15348                highlighted_edits.text,
15349                "Second modified\nNew third line\nFourth updated line"
15350            );
15351            assert_eq!(highlighted_edits.highlights.len(), 3);
15352            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15353            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15354            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15355            for highlight in &highlighted_edits.highlights {
15356                assert_eq!(
15357                    highlight.1.background_color,
15358                    Some(cx.theme().status().created_background)
15359                );
15360            }
15361        },
15362    )
15363    .await;
15364}
15365
15366#[gpui::test]
15367async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15368    init_test(cx, |_| {});
15369
15370    // Deletion
15371    assert_highlighted_edits(
15372        "Hello, world!",
15373        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15374        true,
15375        cx,
15376        |highlighted_edits, cx| {
15377            assert_eq!(highlighted_edits.text, "Hello, world!");
15378            assert_eq!(highlighted_edits.highlights.len(), 1);
15379            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15380            assert_eq!(
15381                highlighted_edits.highlights[0].1.background_color,
15382                Some(cx.theme().status().deleted_background)
15383            );
15384        },
15385    )
15386    .await;
15387
15388    // Insertion
15389    assert_highlighted_edits(
15390        "Hello, world!",
15391        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15392        true,
15393        cx,
15394        |highlighted_edits, cx| {
15395            assert_eq!(highlighted_edits.highlights.len(), 1);
15396            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15397            assert_eq!(
15398                highlighted_edits.highlights[0].1.background_color,
15399                Some(cx.theme().status().created_background)
15400            );
15401        },
15402    )
15403    .await;
15404}
15405
15406async fn assert_highlighted_edits(
15407    text: &str,
15408    edits: Vec<(Range<Point>, String)>,
15409    include_deletions: bool,
15410    cx: &mut TestAppContext,
15411    assertion_fn: impl Fn(HighlightedText, &App),
15412) {
15413    let window = cx.add_window(|window, cx| {
15414        let buffer = MultiBuffer::build_simple(text, cx);
15415        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15416    });
15417    let cx = &mut VisualTestContext::from_window(*window, cx);
15418
15419    let (buffer, snapshot) = window
15420        .update(cx, |editor, _window, cx| {
15421            (
15422                editor.buffer().clone(),
15423                editor.buffer().read(cx).snapshot(cx),
15424            )
15425        })
15426        .unwrap();
15427
15428    let edits = edits
15429        .into_iter()
15430        .map(|(range, edit)| {
15431            (
15432                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15433                edit,
15434            )
15435        })
15436        .collect::<Vec<_>>();
15437
15438    let text_anchor_edits = edits
15439        .clone()
15440        .into_iter()
15441        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15442        .collect::<Vec<_>>();
15443
15444    let edit_preview = window
15445        .update(cx, |_, _window, cx| {
15446            buffer
15447                .read(cx)
15448                .as_singleton()
15449                .unwrap()
15450                .read(cx)
15451                .preview_edits(text_anchor_edits.into(), cx)
15452        })
15453        .unwrap()
15454        .await;
15455
15456    cx.update(|_window, cx| {
15457        let highlighted_edits = inline_completion_edit_text(
15458            &snapshot.as_singleton().unwrap().2,
15459            &edits,
15460            &edit_preview,
15461            include_deletions,
15462            cx,
15463        );
15464        assertion_fn(highlighted_edits, cx)
15465    });
15466}
15467
15468#[gpui::test]
15469async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15470    init_test(cx, |_| {});
15471    let capabilities = lsp::ServerCapabilities {
15472        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15473            prepare_provider: Some(true),
15474            work_done_progress_options: Default::default(),
15475        })),
15476        ..Default::default()
15477    };
15478    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15479
15480    cx.set_state(indoc! {"
15481        struct Fˇoo {}
15482    "});
15483
15484    cx.update_editor(|editor, _, cx| {
15485        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15486        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15487        editor.highlight_background::<DocumentHighlightRead>(
15488            &[highlight_range],
15489            |c| c.editor_document_highlight_read_background,
15490            cx,
15491        );
15492    });
15493
15494    let mut prepare_rename_handler =
15495        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15496            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15497                start: lsp::Position {
15498                    line: 0,
15499                    character: 7,
15500                },
15501                end: lsp::Position {
15502                    line: 0,
15503                    character: 10,
15504                },
15505            })))
15506        });
15507    let prepare_rename_task = cx
15508        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15509        .expect("Prepare rename was not started");
15510    prepare_rename_handler.next().await.unwrap();
15511    prepare_rename_task.await.expect("Prepare rename failed");
15512
15513    let mut rename_handler =
15514        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15515            let edit = lsp::TextEdit {
15516                range: lsp::Range {
15517                    start: lsp::Position {
15518                        line: 0,
15519                        character: 7,
15520                    },
15521                    end: lsp::Position {
15522                        line: 0,
15523                        character: 10,
15524                    },
15525                },
15526                new_text: "FooRenamed".to_string(),
15527            };
15528            Ok(Some(lsp::WorkspaceEdit::new(
15529                // Specify the same edit twice
15530                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15531            )))
15532        });
15533    let rename_task = cx
15534        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15535        .expect("Confirm rename was not started");
15536    rename_handler.next().await.unwrap();
15537    rename_task.await.expect("Confirm rename failed");
15538    cx.run_until_parked();
15539
15540    // Despite two edits, only one is actually applied as those are identical
15541    cx.assert_editor_state(indoc! {"
15542        struct FooRenamedˇ {}
15543    "});
15544}
15545
15546#[gpui::test]
15547async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15548    init_test(cx, |_| {});
15549    // These capabilities indicate that the server does not support prepare rename.
15550    let capabilities = lsp::ServerCapabilities {
15551        rename_provider: Some(lsp::OneOf::Left(true)),
15552        ..Default::default()
15553    };
15554    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15555
15556    cx.set_state(indoc! {"
15557        struct Fˇoo {}
15558    "});
15559
15560    cx.update_editor(|editor, _window, cx| {
15561        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15562        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15563        editor.highlight_background::<DocumentHighlightRead>(
15564            &[highlight_range],
15565            |c| c.editor_document_highlight_read_background,
15566            cx,
15567        );
15568    });
15569
15570    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15571        .expect("Prepare rename was not started")
15572        .await
15573        .expect("Prepare rename failed");
15574
15575    let mut rename_handler =
15576        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15577            let edit = lsp::TextEdit {
15578                range: lsp::Range {
15579                    start: lsp::Position {
15580                        line: 0,
15581                        character: 7,
15582                    },
15583                    end: lsp::Position {
15584                        line: 0,
15585                        character: 10,
15586                    },
15587                },
15588                new_text: "FooRenamed".to_string(),
15589            };
15590            Ok(Some(lsp::WorkspaceEdit::new(
15591                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15592            )))
15593        });
15594    let rename_task = cx
15595        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15596        .expect("Confirm rename was not started");
15597    rename_handler.next().await.unwrap();
15598    rename_task.await.expect("Confirm rename failed");
15599    cx.run_until_parked();
15600
15601    // Correct range is renamed, as `surrounding_word` is used to find it.
15602    cx.assert_editor_state(indoc! {"
15603        struct FooRenamedˇ {}
15604    "});
15605}
15606
15607fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15608    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15609    point..point
15610}
15611
15612fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15613    let (text, ranges) = marked_text_ranges(marked_text, true);
15614    assert_eq!(editor.text(cx), text);
15615    assert_eq!(
15616        editor.selections.ranges(cx),
15617        ranges,
15618        "Assert selections are {}",
15619        marked_text
15620    );
15621}
15622
15623pub fn handle_signature_help_request(
15624    cx: &mut EditorLspTestContext,
15625    mocked_response: lsp::SignatureHelp,
15626) -> impl Future<Output = ()> {
15627    let mut request =
15628        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15629            let mocked_response = mocked_response.clone();
15630            async move { Ok(Some(mocked_response)) }
15631        });
15632
15633    async move {
15634        request.next().await;
15635    }
15636}
15637
15638/// Handle completion request passing a marked string specifying where the completion
15639/// should be triggered from using '|' character, what range should be replaced, and what completions
15640/// should be returned using '<' and '>' to delimit the range
15641pub fn handle_completion_request(
15642    cx: &mut EditorLspTestContext,
15643    marked_string: &str,
15644    completions: Vec<&'static str>,
15645    counter: Arc<AtomicUsize>,
15646) -> impl Future<Output = ()> {
15647    let complete_from_marker: TextRangeMarker = '|'.into();
15648    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15649    let (_, mut marked_ranges) = marked_text_ranges_by(
15650        marked_string,
15651        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15652    );
15653
15654    let complete_from_position =
15655        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15656    let replace_range =
15657        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15658
15659    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15660        let completions = completions.clone();
15661        counter.fetch_add(1, atomic::Ordering::Release);
15662        async move {
15663            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15664            assert_eq!(
15665                params.text_document_position.position,
15666                complete_from_position
15667            );
15668            Ok(Some(lsp::CompletionResponse::Array(
15669                completions
15670                    .iter()
15671                    .map(|completion_text| lsp::CompletionItem {
15672                        label: completion_text.to_string(),
15673                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15674                            range: replace_range,
15675                            new_text: completion_text.to_string(),
15676                        })),
15677                        ..Default::default()
15678                    })
15679                    .collect(),
15680            )))
15681        }
15682    });
15683
15684    async move {
15685        request.next().await;
15686    }
15687}
15688
15689fn handle_resolve_completion_request(
15690    cx: &mut EditorLspTestContext,
15691    edits: Option<Vec<(&'static str, &'static str)>>,
15692) -> impl Future<Output = ()> {
15693    let edits = edits.map(|edits| {
15694        edits
15695            .iter()
15696            .map(|(marked_string, new_text)| {
15697                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15698                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15699                lsp::TextEdit::new(replace_range, new_text.to_string())
15700            })
15701            .collect::<Vec<_>>()
15702    });
15703
15704    let mut request =
15705        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15706            let edits = edits.clone();
15707            async move {
15708                Ok(lsp::CompletionItem {
15709                    additional_text_edits: edits,
15710                    ..Default::default()
15711                })
15712            }
15713        });
15714
15715    async move {
15716        request.next().await;
15717    }
15718}
15719
15720pub(crate) fn update_test_language_settings(
15721    cx: &mut TestAppContext,
15722    f: impl Fn(&mut AllLanguageSettingsContent),
15723) {
15724    cx.update(|cx| {
15725        SettingsStore::update_global(cx, |store, cx| {
15726            store.update_user_settings::<AllLanguageSettings>(cx, f);
15727        });
15728    });
15729}
15730
15731pub(crate) fn update_test_project_settings(
15732    cx: &mut TestAppContext,
15733    f: impl Fn(&mut ProjectSettings),
15734) {
15735    cx.update(|cx| {
15736        SettingsStore::update_global(cx, |store, cx| {
15737            store.update_user_settings::<ProjectSettings>(cx, f);
15738        });
15739    });
15740}
15741
15742pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15743    cx.update(|cx| {
15744        assets::Assets.load_test_fonts(cx);
15745        let store = SettingsStore::test(cx);
15746        cx.set_global(store);
15747        theme::init(theme::LoadThemes::JustBase, cx);
15748        release_channel::init(SemanticVersion::default(), cx);
15749        client::init_settings(cx);
15750        language::init(cx);
15751        Project::init_settings(cx);
15752        workspace::init_settings(cx);
15753        crate::init(cx);
15754    });
15755
15756    update_test_language_settings(cx, f);
15757}
15758
15759#[track_caller]
15760fn assert_hunk_revert(
15761    not_reverted_text_with_selections: &str,
15762    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15763    expected_reverted_text_with_selections: &str,
15764    base_text: &str,
15765    cx: &mut EditorLspTestContext,
15766) {
15767    cx.set_state(not_reverted_text_with_selections);
15768    cx.set_diff_base(base_text);
15769    cx.executor().run_until_parked();
15770
15771    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15772        let snapshot = editor.snapshot(window, cx);
15773        let reverted_hunk_statuses = snapshot
15774            .buffer_snapshot
15775            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15776            .map(|hunk| hunk.status())
15777            .collect::<Vec<_>>();
15778
15779        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15780        reverted_hunk_statuses
15781    });
15782    cx.executor().run_until_parked();
15783    cx.assert_editor_state(expected_reverted_text_with_selections);
15784    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15785}