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                        .flat_map(|c| match c {
11711                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11712                            _ => None,
11713                        })
11714                        .collect::<Vec<String>>(),
11715                    items_out
11716                        .iter()
11717                        .map(|completion| completion.label.clone())
11718                        .collect::<Vec<String>>()
11719                );
11720            }
11721            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11722        }
11723    });
11724    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11725    // with 4 from the end.
11726    assert_eq!(
11727        *resolved_items.lock(),
11728        [
11729            &items_out[0..16],
11730            &items_out[items_out.len() - 4..items_out.len()]
11731        ]
11732        .concat()
11733        .iter()
11734        .cloned()
11735        .collect::<Vec<lsp::CompletionItem>>()
11736    );
11737    resolved_items.lock().clear();
11738
11739    cx.update_editor(|editor, window, cx| {
11740        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11741    });
11742    cx.run_until_parked();
11743    // Completions that have already been resolved are skipped.
11744    assert_eq!(
11745        *resolved_items.lock(),
11746        items_out[items_out.len() - 16..items_out.len() - 4]
11747            .iter()
11748            .cloned()
11749            .collect::<Vec<lsp::CompletionItem>>()
11750    );
11751    resolved_items.lock().clear();
11752}
11753
11754#[gpui::test]
11755async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11756    init_test(cx, |_| {});
11757
11758    let mut cx = EditorLspTestContext::new(
11759        Language::new(
11760            LanguageConfig {
11761                matcher: LanguageMatcher {
11762                    path_suffixes: vec!["jsx".into()],
11763                    ..Default::default()
11764                },
11765                overrides: [(
11766                    "element".into(),
11767                    LanguageConfigOverride {
11768                        word_characters: Override::Set(['-'].into_iter().collect()),
11769                        ..Default::default()
11770                    },
11771                )]
11772                .into_iter()
11773                .collect(),
11774                ..Default::default()
11775            },
11776            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11777        )
11778        .with_override_query("(jsx_self_closing_element) @element")
11779        .unwrap(),
11780        lsp::ServerCapabilities {
11781            completion_provider: Some(lsp::CompletionOptions {
11782                trigger_characters: Some(vec![":".to_string()]),
11783                ..Default::default()
11784            }),
11785            ..Default::default()
11786        },
11787        cx,
11788    )
11789    .await;
11790
11791    cx.lsp
11792        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11793            Ok(Some(lsp::CompletionResponse::Array(vec![
11794                lsp::CompletionItem {
11795                    label: "bg-blue".into(),
11796                    ..Default::default()
11797                },
11798                lsp::CompletionItem {
11799                    label: "bg-red".into(),
11800                    ..Default::default()
11801                },
11802                lsp::CompletionItem {
11803                    label: "bg-yellow".into(),
11804                    ..Default::default()
11805                },
11806            ])))
11807        });
11808
11809    cx.set_state(r#"<p class="bgˇ" />"#);
11810
11811    // Trigger completion when typing a dash, because the dash is an extra
11812    // word character in the 'element' scope, which contains the cursor.
11813    cx.simulate_keystroke("-");
11814    cx.executor().run_until_parked();
11815    cx.update_editor(|editor, _, _| {
11816        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11817        {
11818            assert_eq!(
11819                completion_menu_entries(&menu),
11820                &["bg-red", "bg-blue", "bg-yellow"]
11821            );
11822        } else {
11823            panic!("expected completion menu to be open");
11824        }
11825    });
11826
11827    cx.simulate_keystroke("l");
11828    cx.executor().run_until_parked();
11829    cx.update_editor(|editor, _, _| {
11830        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11831        {
11832            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11833        } else {
11834            panic!("expected completion menu to be open");
11835        }
11836    });
11837
11838    // When filtering completions, consider the character after the '-' to
11839    // be the start of a subword.
11840    cx.set_state(r#"<p class="yelˇ" />"#);
11841    cx.simulate_keystroke("l");
11842    cx.executor().run_until_parked();
11843    cx.update_editor(|editor, _, _| {
11844        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11845        {
11846            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11847        } else {
11848            panic!("expected completion menu to be open");
11849        }
11850    });
11851}
11852
11853fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11854    let entries = menu.entries.borrow();
11855    entries
11856        .iter()
11857        .flat_map(|e| match e {
11858            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11859            _ => None,
11860        })
11861        .collect()
11862}
11863
11864#[gpui::test]
11865async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11866    init_test(cx, |settings| {
11867        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11868            FormatterList(vec![Formatter::Prettier].into()),
11869        ))
11870    });
11871
11872    let fs = FakeFs::new(cx.executor());
11873    fs.insert_file("/file.ts", Default::default()).await;
11874
11875    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11876    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11877
11878    language_registry.add(Arc::new(Language::new(
11879        LanguageConfig {
11880            name: "TypeScript".into(),
11881            matcher: LanguageMatcher {
11882                path_suffixes: vec!["ts".to_string()],
11883                ..Default::default()
11884            },
11885            ..Default::default()
11886        },
11887        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11888    )));
11889    update_test_language_settings(cx, |settings| {
11890        settings.defaults.prettier = Some(PrettierSettings {
11891            allowed: true,
11892            ..PrettierSettings::default()
11893        });
11894    });
11895
11896    let test_plugin = "test_plugin";
11897    let _ = language_registry.register_fake_lsp(
11898        "TypeScript",
11899        FakeLspAdapter {
11900            prettier_plugins: vec![test_plugin],
11901            ..Default::default()
11902        },
11903    );
11904
11905    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11906    let buffer = project
11907        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11908        .await
11909        .unwrap();
11910
11911    let buffer_text = "one\ntwo\nthree\n";
11912    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11913    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11914    editor.update_in(cx, |editor, window, cx| {
11915        editor.set_text(buffer_text, window, cx)
11916    });
11917
11918    editor
11919        .update_in(cx, |editor, window, cx| {
11920            editor.perform_format(
11921                project.clone(),
11922                FormatTrigger::Manual,
11923                FormatTarget::Buffers,
11924                window,
11925                cx,
11926            )
11927        })
11928        .unwrap()
11929        .await;
11930    assert_eq!(
11931        editor.update(cx, |editor, cx| editor.text(cx)),
11932        buffer_text.to_string() + prettier_format_suffix,
11933        "Test prettier formatting was not applied to the original buffer text",
11934    );
11935
11936    update_test_language_settings(cx, |settings| {
11937        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11938    });
11939    let format = editor.update_in(cx, |editor, window, cx| {
11940        editor.perform_format(
11941            project.clone(),
11942            FormatTrigger::Manual,
11943            FormatTarget::Buffers,
11944            window,
11945            cx,
11946        )
11947    });
11948    format.await.unwrap();
11949    assert_eq!(
11950        editor.update(cx, |editor, cx| editor.text(cx)),
11951        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11952        "Autoformatting (via test prettier) was not applied to the original buffer text",
11953    );
11954}
11955
11956#[gpui::test]
11957async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11958    init_test(cx, |_| {});
11959    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11960    let base_text = indoc! {r#"
11961        struct Row;
11962        struct Row1;
11963        struct Row2;
11964
11965        struct Row4;
11966        struct Row5;
11967        struct Row6;
11968
11969        struct Row8;
11970        struct Row9;
11971        struct Row10;"#};
11972
11973    // When addition hunks are not adjacent to carets, no hunk revert is performed
11974    assert_hunk_revert(
11975        indoc! {r#"struct Row;
11976                   struct Row1;
11977                   struct Row1.1;
11978                   struct Row1.2;
11979                   struct Row2;ˇ
11980
11981                   struct Row4;
11982                   struct Row5;
11983                   struct Row6;
11984
11985                   struct Row8;
11986                   ˇstruct Row9;
11987                   struct Row9.1;
11988                   struct Row9.2;
11989                   struct Row9.3;
11990                   struct Row10;"#},
11991        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11992        indoc! {r#"struct Row;
11993                   struct Row1;
11994                   struct Row1.1;
11995                   struct Row1.2;
11996                   struct Row2;ˇ
11997
11998                   struct Row4;
11999                   struct Row5;
12000                   struct Row6;
12001
12002                   struct Row8;
12003                   ˇstruct Row9;
12004                   struct Row9.1;
12005                   struct Row9.2;
12006                   struct Row9.3;
12007                   struct Row10;"#},
12008        base_text,
12009        &mut cx,
12010    );
12011    // Same for selections
12012    assert_hunk_revert(
12013        indoc! {r#"struct Row;
12014                   struct Row1;
12015                   struct Row2;
12016                   struct Row2.1;
12017                   struct Row2.2;
12018                   «ˇ
12019                   struct Row4;
12020                   struct» Row5;
12021                   «struct Row6;
12022                   ˇ»
12023                   struct Row9.1;
12024                   struct Row9.2;
12025                   struct Row9.3;
12026                   struct Row8;
12027                   struct Row9;
12028                   struct Row10;"#},
12029        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12030        indoc! {r#"struct Row;
12031                   struct Row1;
12032                   struct Row2;
12033                   struct Row2.1;
12034                   struct Row2.2;
12035                   «ˇ
12036                   struct Row4;
12037                   struct» Row5;
12038                   «struct Row6;
12039                   ˇ»
12040                   struct Row9.1;
12041                   struct Row9.2;
12042                   struct Row9.3;
12043                   struct Row8;
12044                   struct Row9;
12045                   struct Row10;"#},
12046        base_text,
12047        &mut cx,
12048    );
12049
12050    // When carets and selections intersect the addition hunks, those are reverted.
12051    // Adjacent carets got merged.
12052    assert_hunk_revert(
12053        indoc! {r#"struct Row;
12054                   ˇ// something on the top
12055                   struct Row1;
12056                   struct Row2;
12057                   struct Roˇw3.1;
12058                   struct Row2.2;
12059                   struct Row2.3;ˇ
12060
12061                   struct Row4;
12062                   struct ˇRow5.1;
12063                   struct Row5.2;
12064                   struct «Rowˇ»5.3;
12065                   struct Row5;
12066                   struct Row6;
12067                   ˇ
12068                   struct Row9.1;
12069                   struct «Rowˇ»9.2;
12070                   struct «ˇRow»9.3;
12071                   struct Row8;
12072                   struct Row9;
12073                   «ˇ// something on bottom»
12074                   struct Row10;"#},
12075        vec![
12076            DiffHunkStatus::Added,
12077            DiffHunkStatus::Added,
12078            DiffHunkStatus::Added,
12079            DiffHunkStatus::Added,
12080            DiffHunkStatus::Added,
12081        ],
12082        indoc! {r#"struct Row;
12083                   ˇstruct Row1;
12084                   struct Row2;
12085                   ˇ
12086                   struct Row4;
12087                   ˇstruct Row5;
12088                   struct Row6;
12089                   ˇ
12090                   ˇstruct Row8;
12091                   struct Row9;
12092                   ˇstruct Row10;"#},
12093        base_text,
12094        &mut cx,
12095    );
12096}
12097
12098#[gpui::test]
12099async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12100    init_test(cx, |_| {});
12101    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12102    let base_text = indoc! {r#"
12103        struct Row;
12104        struct Row1;
12105        struct Row2;
12106
12107        struct Row4;
12108        struct Row5;
12109        struct Row6;
12110
12111        struct Row8;
12112        struct Row9;
12113        struct Row10;"#};
12114
12115    // Modification hunks behave the same as the addition ones.
12116    assert_hunk_revert(
12117        indoc! {r#"struct Row;
12118                   struct Row1;
12119                   struct Row33;
12120                   ˇ
12121                   struct Row4;
12122                   struct Row5;
12123                   struct Row6;
12124                   ˇ
12125                   struct Row99;
12126                   struct Row9;
12127                   struct Row10;"#},
12128        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12129        indoc! {r#"struct Row;
12130                   struct Row1;
12131                   struct Row33;
12132                   ˇ
12133                   struct Row4;
12134                   struct Row5;
12135                   struct Row6;
12136                   ˇ
12137                   struct Row99;
12138                   struct Row9;
12139                   struct Row10;"#},
12140        base_text,
12141        &mut cx,
12142    );
12143    assert_hunk_revert(
12144        indoc! {r#"struct Row;
12145                   struct Row1;
12146                   struct Row33;
12147                   «ˇ
12148                   struct Row4;
12149                   struct» Row5;
12150                   «struct Row6;
12151                   ˇ»
12152                   struct Row99;
12153                   struct Row9;
12154                   struct Row10;"#},
12155        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12156        indoc! {r#"struct Row;
12157                   struct Row1;
12158                   struct Row33;
12159                   «ˇ
12160                   struct Row4;
12161                   struct» Row5;
12162                   «struct Row6;
12163                   ˇ»
12164                   struct Row99;
12165                   struct Row9;
12166                   struct Row10;"#},
12167        base_text,
12168        &mut cx,
12169    );
12170
12171    assert_hunk_revert(
12172        indoc! {r#"ˇstruct Row1.1;
12173                   struct Row1;
12174                   «ˇstr»uct Row22;
12175
12176                   struct ˇRow44;
12177                   struct Row5;
12178                   struct «Rˇ»ow66;ˇ
12179
12180                   «struˇ»ct Row88;
12181                   struct Row9;
12182                   struct Row1011;ˇ"#},
12183        vec![
12184            DiffHunkStatus::Modified,
12185            DiffHunkStatus::Modified,
12186            DiffHunkStatus::Modified,
12187            DiffHunkStatus::Modified,
12188            DiffHunkStatus::Modified,
12189            DiffHunkStatus::Modified,
12190        ],
12191        indoc! {r#"struct Row;
12192                   ˇstruct Row1;
12193                   struct Row2;
12194                   ˇ
12195                   struct Row4;
12196                   ˇstruct Row5;
12197                   struct Row6;
12198                   ˇ
12199                   struct Row8;
12200                   ˇstruct Row9;
12201                   struct Row10;ˇ"#},
12202        base_text,
12203        &mut cx,
12204    );
12205}
12206
12207#[gpui::test]
12208async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12209    init_test(cx, |_| {});
12210    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12211    let base_text = indoc! {r#"
12212        one
12213
12214        two
12215        three
12216        "#};
12217
12218    cx.set_diff_base(base_text);
12219    cx.set_state("\nˇ\n");
12220    cx.executor().run_until_parked();
12221    cx.update_editor(|editor, _window, cx| {
12222        editor.expand_selected_diff_hunks(cx);
12223    });
12224    cx.executor().run_until_parked();
12225    cx.update_editor(|editor, window, cx| {
12226        editor.backspace(&Default::default(), window, cx);
12227    });
12228    cx.run_until_parked();
12229    cx.assert_state_with_diff(
12230        indoc! {r#"
12231
12232        - two
12233        - threeˇ
12234        +
12235        "#}
12236        .to_string(),
12237    );
12238}
12239
12240#[gpui::test]
12241async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12242    init_test(cx, |_| {});
12243    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12244    let base_text = indoc! {r#"struct Row;
12245struct Row1;
12246struct Row2;
12247
12248struct Row4;
12249struct Row5;
12250struct Row6;
12251
12252struct Row8;
12253struct Row9;
12254struct Row10;"#};
12255
12256    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12257    assert_hunk_revert(
12258        indoc! {r#"struct Row;
12259                   struct Row2;
12260
12261                   ˇstruct Row4;
12262                   struct Row5;
12263                   struct Row6;
12264                   ˇ
12265                   struct Row8;
12266                   struct Row10;"#},
12267        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12268        indoc! {r#"struct Row;
12269                   struct Row2;
12270
12271                   ˇstruct Row4;
12272                   struct Row5;
12273                   struct Row6;
12274                   ˇ
12275                   struct Row8;
12276                   struct Row10;"#},
12277        base_text,
12278        &mut cx,
12279    );
12280    assert_hunk_revert(
12281        indoc! {r#"struct Row;
12282                   struct Row2;
12283
12284                   «ˇstruct Row4;
12285                   struct» Row5;
12286                   «struct Row6;
12287                   ˇ»
12288                   struct Row8;
12289                   struct Row10;"#},
12290        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12291        indoc! {r#"struct Row;
12292                   struct Row2;
12293
12294                   «ˇstruct Row4;
12295                   struct» Row5;
12296                   «struct Row6;
12297                   ˇ»
12298                   struct Row8;
12299                   struct Row10;"#},
12300        base_text,
12301        &mut cx,
12302    );
12303
12304    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12305    assert_hunk_revert(
12306        indoc! {r#"struct Row;
12307                   ˇstruct Row2;
12308
12309                   struct Row4;
12310                   struct Row5;
12311                   struct Row6;
12312
12313                   struct Row8;ˇ
12314                   struct Row10;"#},
12315        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12316        indoc! {r#"struct Row;
12317                   struct Row1;
12318                   ˇstruct Row2;
12319
12320                   struct Row4;
12321                   struct Row5;
12322                   struct Row6;
12323
12324                   struct Row8;ˇ
12325                   struct Row9;
12326                   struct Row10;"#},
12327        base_text,
12328        &mut cx,
12329    );
12330    assert_hunk_revert(
12331        indoc! {r#"struct Row;
12332                   struct Row2«ˇ;
12333                   struct Row4;
12334                   struct» Row5;
12335                   «struct Row6;
12336
12337                   struct Row8;ˇ»
12338                   struct Row10;"#},
12339        vec![
12340            DiffHunkStatus::Removed,
12341            DiffHunkStatus::Removed,
12342            DiffHunkStatus::Removed,
12343        ],
12344        indoc! {r#"struct Row;
12345                   struct Row1;
12346                   struct Row2«ˇ;
12347
12348                   struct Row4;
12349                   struct» Row5;
12350                   «struct Row6;
12351
12352                   struct Row8;ˇ»
12353                   struct Row9;
12354                   struct Row10;"#},
12355        base_text,
12356        &mut cx,
12357    );
12358}
12359
12360#[gpui::test]
12361async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12362    init_test(cx, |_| {});
12363
12364    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12365    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12366    let base_text_3 =
12367        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12368
12369    let text_1 = edit_first_char_of_every_line(base_text_1);
12370    let text_2 = edit_first_char_of_every_line(base_text_2);
12371    let text_3 = edit_first_char_of_every_line(base_text_3);
12372
12373    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12374    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12375    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12376
12377    let multibuffer = cx.new(|cx| {
12378        let mut multibuffer = MultiBuffer::new(ReadWrite);
12379        multibuffer.push_excerpts(
12380            buffer_1.clone(),
12381            [
12382                ExcerptRange {
12383                    context: Point::new(0, 0)..Point::new(3, 0),
12384                    primary: None,
12385                },
12386                ExcerptRange {
12387                    context: Point::new(5, 0)..Point::new(7, 0),
12388                    primary: None,
12389                },
12390                ExcerptRange {
12391                    context: Point::new(9, 0)..Point::new(10, 4),
12392                    primary: None,
12393                },
12394            ],
12395            cx,
12396        );
12397        multibuffer.push_excerpts(
12398            buffer_2.clone(),
12399            [
12400                ExcerptRange {
12401                    context: Point::new(0, 0)..Point::new(3, 0),
12402                    primary: None,
12403                },
12404                ExcerptRange {
12405                    context: Point::new(5, 0)..Point::new(7, 0),
12406                    primary: None,
12407                },
12408                ExcerptRange {
12409                    context: Point::new(9, 0)..Point::new(10, 4),
12410                    primary: None,
12411                },
12412            ],
12413            cx,
12414        );
12415        multibuffer.push_excerpts(
12416            buffer_3.clone(),
12417            [
12418                ExcerptRange {
12419                    context: Point::new(0, 0)..Point::new(3, 0),
12420                    primary: None,
12421                },
12422                ExcerptRange {
12423                    context: Point::new(5, 0)..Point::new(7, 0),
12424                    primary: None,
12425                },
12426                ExcerptRange {
12427                    context: Point::new(9, 0)..Point::new(10, 4),
12428                    primary: None,
12429                },
12430            ],
12431            cx,
12432        );
12433        multibuffer
12434    });
12435
12436    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12437    editor.update_in(cx, |editor, _window, cx| {
12438        for (buffer, diff_base) in [
12439            (buffer_1.clone(), base_text_1),
12440            (buffer_2.clone(), base_text_2),
12441            (buffer_3.clone(), base_text_3),
12442        ] {
12443            let change_set = cx
12444                .new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx));
12445            editor
12446                .buffer
12447                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12448        }
12449    });
12450    cx.executor().run_until_parked();
12451
12452    editor.update_in(cx, |editor, window, cx| {
12453        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}");
12454        editor.select_all(&SelectAll, window, cx);
12455        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12456    });
12457    cx.executor().run_until_parked();
12458
12459    // When all ranges are selected, all buffer hunks are reverted.
12460    editor.update(cx, |editor, cx| {
12461        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");
12462    });
12463    buffer_1.update(cx, |buffer, _| {
12464        assert_eq!(buffer.text(), base_text_1);
12465    });
12466    buffer_2.update(cx, |buffer, _| {
12467        assert_eq!(buffer.text(), base_text_2);
12468    });
12469    buffer_3.update(cx, |buffer, _| {
12470        assert_eq!(buffer.text(), base_text_3);
12471    });
12472
12473    editor.update_in(cx, |editor, window, cx| {
12474        editor.undo(&Default::default(), window, cx);
12475    });
12476
12477    editor.update_in(cx, |editor, window, cx| {
12478        editor.change_selections(None, window, cx, |s| {
12479            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12480        });
12481        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12482    });
12483
12484    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12485    // but not affect buffer_2 and its related excerpts.
12486    editor.update(cx, |editor, cx| {
12487        assert_eq!(
12488            editor.text(cx),
12489            "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}"
12490        );
12491    });
12492    buffer_1.update(cx, |buffer, _| {
12493        assert_eq!(buffer.text(), base_text_1);
12494    });
12495    buffer_2.update(cx, |buffer, _| {
12496        assert_eq!(
12497            buffer.text(),
12498            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12499        );
12500    });
12501    buffer_3.update(cx, |buffer, _| {
12502        assert_eq!(
12503            buffer.text(),
12504            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12505        );
12506    });
12507
12508    fn edit_first_char_of_every_line(text: &str) -> String {
12509        text.split('\n')
12510            .map(|line| format!("X{}", &line[1..]))
12511            .collect::<Vec<_>>()
12512            .join("\n")
12513    }
12514}
12515
12516#[gpui::test]
12517async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12518    init_test(cx, |_| {});
12519
12520    let cols = 4;
12521    let rows = 10;
12522    let sample_text_1 = sample_text(rows, cols, 'a');
12523    assert_eq!(
12524        sample_text_1,
12525        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12526    );
12527    let sample_text_2 = sample_text(rows, cols, 'l');
12528    assert_eq!(
12529        sample_text_2,
12530        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12531    );
12532    let sample_text_3 = sample_text(rows, cols, 'v');
12533    assert_eq!(
12534        sample_text_3,
12535        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12536    );
12537
12538    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12539    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12540    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12541
12542    let multi_buffer = cx.new(|cx| {
12543        let mut multibuffer = MultiBuffer::new(ReadWrite);
12544        multibuffer.push_excerpts(
12545            buffer_1.clone(),
12546            [
12547                ExcerptRange {
12548                    context: Point::new(0, 0)..Point::new(3, 0),
12549                    primary: None,
12550                },
12551                ExcerptRange {
12552                    context: Point::new(5, 0)..Point::new(7, 0),
12553                    primary: None,
12554                },
12555                ExcerptRange {
12556                    context: Point::new(9, 0)..Point::new(10, 4),
12557                    primary: None,
12558                },
12559            ],
12560            cx,
12561        );
12562        multibuffer.push_excerpts(
12563            buffer_2.clone(),
12564            [
12565                ExcerptRange {
12566                    context: Point::new(0, 0)..Point::new(3, 0),
12567                    primary: None,
12568                },
12569                ExcerptRange {
12570                    context: Point::new(5, 0)..Point::new(7, 0),
12571                    primary: None,
12572                },
12573                ExcerptRange {
12574                    context: Point::new(9, 0)..Point::new(10, 4),
12575                    primary: None,
12576                },
12577            ],
12578            cx,
12579        );
12580        multibuffer.push_excerpts(
12581            buffer_3.clone(),
12582            [
12583                ExcerptRange {
12584                    context: Point::new(0, 0)..Point::new(3, 0),
12585                    primary: None,
12586                },
12587                ExcerptRange {
12588                    context: Point::new(5, 0)..Point::new(7, 0),
12589                    primary: None,
12590                },
12591                ExcerptRange {
12592                    context: Point::new(9, 0)..Point::new(10, 4),
12593                    primary: None,
12594                },
12595            ],
12596            cx,
12597        );
12598        multibuffer
12599    });
12600
12601    let fs = FakeFs::new(cx.executor());
12602    fs.insert_tree(
12603        "/a",
12604        json!({
12605            "main.rs": sample_text_1,
12606            "other.rs": sample_text_2,
12607            "lib.rs": sample_text_3,
12608        }),
12609    )
12610    .await;
12611    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12612    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12613    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12614    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12615        Editor::new(
12616            EditorMode::Full,
12617            multi_buffer,
12618            Some(project.clone()),
12619            true,
12620            window,
12621            cx,
12622        )
12623    });
12624    let multibuffer_item_id = workspace
12625        .update(cx, |workspace, window, cx| {
12626            assert!(
12627                workspace.active_item(cx).is_none(),
12628                "active item should be None before the first item is added"
12629            );
12630            workspace.add_item_to_active_pane(
12631                Box::new(multi_buffer_editor.clone()),
12632                None,
12633                true,
12634                window,
12635                cx,
12636            );
12637            let active_item = workspace
12638                .active_item(cx)
12639                .expect("should have an active item after adding the multi buffer");
12640            assert!(
12641                !active_item.is_singleton(cx),
12642                "A multi buffer was expected to active after adding"
12643            );
12644            active_item.item_id()
12645        })
12646        .unwrap();
12647    cx.executor().run_until_parked();
12648
12649    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12650        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12651            s.select_ranges(Some(1..2))
12652        });
12653        editor.open_excerpts(&OpenExcerpts, window, cx);
12654    });
12655    cx.executor().run_until_parked();
12656    let first_item_id = workspace
12657        .update(cx, |workspace, window, cx| {
12658            let active_item = workspace
12659                .active_item(cx)
12660                .expect("should have an active item after navigating into the 1st buffer");
12661            let first_item_id = active_item.item_id();
12662            assert_ne!(
12663                first_item_id, multibuffer_item_id,
12664                "Should navigate into the 1st buffer and activate it"
12665            );
12666            assert!(
12667                active_item.is_singleton(cx),
12668                "New active item should be a singleton buffer"
12669            );
12670            assert_eq!(
12671                active_item
12672                    .act_as::<Editor>(cx)
12673                    .expect("should have navigated into an editor for the 1st buffer")
12674                    .read(cx)
12675                    .text(cx),
12676                sample_text_1
12677            );
12678
12679            workspace
12680                .go_back(workspace.active_pane().downgrade(), window, cx)
12681                .detach_and_log_err(cx);
12682
12683            first_item_id
12684        })
12685        .unwrap();
12686    cx.executor().run_until_parked();
12687    workspace
12688        .update(cx, |workspace, _, cx| {
12689            let active_item = workspace
12690                .active_item(cx)
12691                .expect("should have an active item after navigating back");
12692            assert_eq!(
12693                active_item.item_id(),
12694                multibuffer_item_id,
12695                "Should navigate back to the multi buffer"
12696            );
12697            assert!(!active_item.is_singleton(cx));
12698        })
12699        .unwrap();
12700
12701    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12702        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12703            s.select_ranges(Some(39..40))
12704        });
12705        editor.open_excerpts(&OpenExcerpts, window, cx);
12706    });
12707    cx.executor().run_until_parked();
12708    let second_item_id = workspace
12709        .update(cx, |workspace, window, cx| {
12710            let active_item = workspace
12711                .active_item(cx)
12712                .expect("should have an active item after navigating into the 2nd buffer");
12713            let second_item_id = active_item.item_id();
12714            assert_ne!(
12715                second_item_id, multibuffer_item_id,
12716                "Should navigate away from the multibuffer"
12717            );
12718            assert_ne!(
12719                second_item_id, first_item_id,
12720                "Should navigate into the 2nd buffer and activate it"
12721            );
12722            assert!(
12723                active_item.is_singleton(cx),
12724                "New active item should be a singleton buffer"
12725            );
12726            assert_eq!(
12727                active_item
12728                    .act_as::<Editor>(cx)
12729                    .expect("should have navigated into an editor")
12730                    .read(cx)
12731                    .text(cx),
12732                sample_text_2
12733            );
12734
12735            workspace
12736                .go_back(workspace.active_pane().downgrade(), window, cx)
12737                .detach_and_log_err(cx);
12738
12739            second_item_id
12740        })
12741        .unwrap();
12742    cx.executor().run_until_parked();
12743    workspace
12744        .update(cx, |workspace, _, cx| {
12745            let active_item = workspace
12746                .active_item(cx)
12747                .expect("should have an active item after navigating back from the 2nd buffer");
12748            assert_eq!(
12749                active_item.item_id(),
12750                multibuffer_item_id,
12751                "Should navigate back from the 2nd buffer to the multi buffer"
12752            );
12753            assert!(!active_item.is_singleton(cx));
12754        })
12755        .unwrap();
12756
12757    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12758        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12759            s.select_ranges(Some(70..70))
12760        });
12761        editor.open_excerpts(&OpenExcerpts, window, cx);
12762    });
12763    cx.executor().run_until_parked();
12764    workspace
12765        .update(cx, |workspace, window, cx| {
12766            let active_item = workspace
12767                .active_item(cx)
12768                .expect("should have an active item after navigating into the 3rd buffer");
12769            let third_item_id = active_item.item_id();
12770            assert_ne!(
12771                third_item_id, multibuffer_item_id,
12772                "Should navigate into the 3rd buffer and activate it"
12773            );
12774            assert_ne!(third_item_id, first_item_id);
12775            assert_ne!(third_item_id, second_item_id);
12776            assert!(
12777                active_item.is_singleton(cx),
12778                "New active item should be a singleton buffer"
12779            );
12780            assert_eq!(
12781                active_item
12782                    .act_as::<Editor>(cx)
12783                    .expect("should have navigated into an editor")
12784                    .read(cx)
12785                    .text(cx),
12786                sample_text_3
12787            );
12788
12789            workspace
12790                .go_back(workspace.active_pane().downgrade(), window, cx)
12791                .detach_and_log_err(cx);
12792        })
12793        .unwrap();
12794    cx.executor().run_until_parked();
12795    workspace
12796        .update(cx, |workspace, _, cx| {
12797            let active_item = workspace
12798                .active_item(cx)
12799                .expect("should have an active item after navigating back from the 3rd buffer");
12800            assert_eq!(
12801                active_item.item_id(),
12802                multibuffer_item_id,
12803                "Should navigate back from the 3rd buffer to the multi buffer"
12804            );
12805            assert!(!active_item.is_singleton(cx));
12806        })
12807        .unwrap();
12808}
12809
12810#[gpui::test]
12811async fn test_toggle_selected_diff_hunks(
12812    executor: BackgroundExecutor,
12813    cx: &mut gpui::TestAppContext,
12814) {
12815    init_test(cx, |_| {});
12816
12817    let mut cx = EditorTestContext::new(cx).await;
12818
12819    let diff_base = r#"
12820        use some::mod;
12821
12822        const A: u32 = 42;
12823
12824        fn main() {
12825            println!("hello");
12826
12827            println!("world");
12828        }
12829        "#
12830    .unindent();
12831
12832    cx.set_state(
12833        &r#"
12834        use some::modified;
12835
12836        ˇ
12837        fn main() {
12838            println!("hello there");
12839
12840            println!("around the");
12841            println!("world");
12842        }
12843        "#
12844        .unindent(),
12845    );
12846
12847    cx.set_diff_base(&diff_base);
12848    executor.run_until_parked();
12849
12850    cx.update_editor(|editor, window, cx| {
12851        editor.go_to_next_hunk(&GoToHunk, window, cx);
12852        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12853    });
12854    executor.run_until_parked();
12855    cx.assert_state_with_diff(
12856        r#"
12857          use some::modified;
12858
12859
12860          fn main() {
12861        -     println!("hello");
12862        + ˇ    println!("hello there");
12863
12864              println!("around the");
12865              println!("world");
12866          }
12867        "#
12868        .unindent(),
12869    );
12870
12871    cx.update_editor(|editor, window, cx| {
12872        for _ in 0..2 {
12873            editor.go_to_next_hunk(&GoToHunk, window, cx);
12874            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12875        }
12876    });
12877    executor.run_until_parked();
12878    cx.assert_state_with_diff(
12879        r#"
12880        - use some::mod;
12881        + ˇuse some::modified;
12882
12883
12884          fn main() {
12885        -     println!("hello");
12886        +     println!("hello there");
12887
12888        +     println!("around the");
12889              println!("world");
12890          }
12891        "#
12892        .unindent(),
12893    );
12894
12895    cx.update_editor(|editor, window, cx| {
12896        editor.go_to_next_hunk(&GoToHunk, window, cx);
12897        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12898    });
12899    executor.run_until_parked();
12900    cx.assert_state_with_diff(
12901        r#"
12902        - use some::mod;
12903        + use some::modified;
12904
12905        - const A: u32 = 42;
12906          ˇ
12907          fn main() {
12908        -     println!("hello");
12909        +     println!("hello there");
12910
12911        +     println!("around the");
12912              println!("world");
12913          }
12914        "#
12915        .unindent(),
12916    );
12917
12918    cx.update_editor(|editor, window, cx| {
12919        editor.cancel(&Cancel, window, cx);
12920    });
12921
12922    cx.assert_state_with_diff(
12923        r#"
12924          use some::modified;
12925
12926          ˇ
12927          fn main() {
12928              println!("hello there");
12929
12930              println!("around the");
12931              println!("world");
12932          }
12933        "#
12934        .unindent(),
12935    );
12936}
12937
12938#[gpui::test]
12939async fn test_diff_base_change_with_expanded_diff_hunks(
12940    executor: BackgroundExecutor,
12941    cx: &mut gpui::TestAppContext,
12942) {
12943    init_test(cx, |_| {});
12944
12945    let mut cx = EditorTestContext::new(cx).await;
12946
12947    let diff_base = r#"
12948        use some::mod1;
12949        use some::mod2;
12950
12951        const A: u32 = 42;
12952        const B: u32 = 42;
12953        const C: u32 = 42;
12954
12955        fn main() {
12956            println!("hello");
12957
12958            println!("world");
12959        }
12960        "#
12961    .unindent();
12962
12963    cx.set_state(
12964        &r#"
12965        use some::mod2;
12966
12967        const A: u32 = 42;
12968        const C: u32 = 42;
12969
12970        fn main(ˇ) {
12971            //println!("hello");
12972
12973            println!("world");
12974            //
12975            //
12976        }
12977        "#
12978        .unindent(),
12979    );
12980
12981    cx.set_diff_base(&diff_base);
12982    executor.run_until_parked();
12983
12984    cx.update_editor(|editor, window, cx| {
12985        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
12986    });
12987    executor.run_until_parked();
12988    cx.assert_state_with_diff(
12989        r#"
12990        - use some::mod1;
12991          use some::mod2;
12992
12993          const A: u32 = 42;
12994        - const B: u32 = 42;
12995          const C: u32 = 42;
12996
12997          fn main(ˇ) {
12998        -     println!("hello");
12999        +     //println!("hello");
13000
13001              println!("world");
13002        +     //
13003        +     //
13004          }
13005        "#
13006        .unindent(),
13007    );
13008
13009    cx.set_diff_base("new diff base!");
13010    executor.run_until_parked();
13011    cx.assert_state_with_diff(
13012        r#"
13013          use some::mod2;
13014
13015          const A: u32 = 42;
13016          const C: u32 = 42;
13017
13018          fn main(ˇ) {
13019              //println!("hello");
13020
13021              println!("world");
13022              //
13023              //
13024          }
13025        "#
13026        .unindent(),
13027    );
13028
13029    cx.update_editor(|editor, window, cx| {
13030        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13031    });
13032    executor.run_until_parked();
13033    cx.assert_state_with_diff(
13034        r#"
13035        - new diff base!
13036        + use some::mod2;
13037        +
13038        + const A: u32 = 42;
13039        + const C: u32 = 42;
13040        +
13041        + fn main(ˇ) {
13042        +     //println!("hello");
13043        +
13044        +     println!("world");
13045        +     //
13046        +     //
13047        + }
13048        "#
13049        .unindent(),
13050    );
13051}
13052
13053#[gpui::test]
13054async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13055    init_test(cx, |_| {});
13056
13057    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13058    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13059    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13060    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13061    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13062    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13063
13064    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13065    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13066    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13067
13068    let multi_buffer = cx.new(|cx| {
13069        let mut multibuffer = MultiBuffer::new(ReadWrite);
13070        multibuffer.push_excerpts(
13071            buffer_1.clone(),
13072            [
13073                ExcerptRange {
13074                    context: Point::new(0, 0)..Point::new(3, 0),
13075                    primary: None,
13076                },
13077                ExcerptRange {
13078                    context: Point::new(5, 0)..Point::new(7, 0),
13079                    primary: None,
13080                },
13081                ExcerptRange {
13082                    context: Point::new(9, 0)..Point::new(10, 3),
13083                    primary: None,
13084                },
13085            ],
13086            cx,
13087        );
13088        multibuffer.push_excerpts(
13089            buffer_2.clone(),
13090            [
13091                ExcerptRange {
13092                    context: Point::new(0, 0)..Point::new(3, 0),
13093                    primary: None,
13094                },
13095                ExcerptRange {
13096                    context: Point::new(5, 0)..Point::new(7, 0),
13097                    primary: None,
13098                },
13099                ExcerptRange {
13100                    context: Point::new(9, 0)..Point::new(10, 3),
13101                    primary: None,
13102                },
13103            ],
13104            cx,
13105        );
13106        multibuffer.push_excerpts(
13107            buffer_3.clone(),
13108            [
13109                ExcerptRange {
13110                    context: Point::new(0, 0)..Point::new(3, 0),
13111                    primary: None,
13112                },
13113                ExcerptRange {
13114                    context: Point::new(5, 0)..Point::new(7, 0),
13115                    primary: None,
13116                },
13117                ExcerptRange {
13118                    context: Point::new(9, 0)..Point::new(10, 3),
13119                    primary: None,
13120                },
13121            ],
13122            cx,
13123        );
13124        multibuffer
13125    });
13126
13127    let editor = cx.add_window(|window, cx| {
13128        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13129    });
13130    editor
13131        .update(cx, |editor, _window, cx| {
13132            for (buffer, diff_base) in [
13133                (buffer_1.clone(), file_1_old),
13134                (buffer_2.clone(), file_2_old),
13135                (buffer_3.clone(), file_3_old),
13136            ] {
13137                let change_set = cx.new(|cx| {
13138                    BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
13139                });
13140                editor
13141                    .buffer
13142                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13143            }
13144        })
13145        .unwrap();
13146
13147    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13148    cx.run_until_parked();
13149
13150    cx.assert_editor_state(
13151        &"
13152            ˇaaa
13153            ccc
13154            ddd
13155
13156            ggg
13157            hhh
13158
13159
13160            lll
13161            mmm
13162            NNN
13163
13164            qqq
13165            rrr
13166
13167            uuu
13168            111
13169            222
13170            333
13171
13172            666
13173            777
13174
13175            000
13176            !!!"
13177        .unindent(),
13178    );
13179
13180    cx.update_editor(|editor, window, cx| {
13181        editor.select_all(&SelectAll, window, cx);
13182        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13183    });
13184    cx.executor().run_until_parked();
13185
13186    cx.assert_state_with_diff(
13187        "
13188            «aaa
13189          - bbb
13190            ccc
13191            ddd
13192
13193            ggg
13194            hhh
13195
13196
13197            lll
13198            mmm
13199          - nnn
13200          + NNN
13201
13202            qqq
13203            rrr
13204
13205            uuu
13206            111
13207            222
13208            333
13209
13210          + 666
13211            777
13212
13213            000
13214            !!!ˇ»"
13215            .unindent(),
13216    );
13217}
13218
13219#[gpui::test]
13220async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13221    init_test(cx, |_| {});
13222
13223    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13224    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
13225
13226    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13227    let multi_buffer = cx.new(|cx| {
13228        let mut multibuffer = MultiBuffer::new(ReadWrite);
13229        multibuffer.push_excerpts(
13230            buffer.clone(),
13231            [
13232                ExcerptRange {
13233                    context: Point::new(0, 0)..Point::new(2, 0),
13234                    primary: None,
13235                },
13236                ExcerptRange {
13237                    context: Point::new(5, 0)..Point::new(7, 0),
13238                    primary: None,
13239                },
13240            ],
13241            cx,
13242        );
13243        multibuffer
13244    });
13245
13246    let editor = cx.add_window(|window, cx| {
13247        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13248    });
13249    editor
13250        .update(cx, |editor, _window, cx| {
13251            let change_set =
13252                cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
13253            editor
13254                .buffer
13255                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13256        })
13257        .unwrap();
13258
13259    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13260    cx.run_until_parked();
13261
13262    cx.update_editor(|editor, window, cx| {
13263        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13264    });
13265    cx.executor().run_until_parked();
13266
13267    cx.assert_state_with_diff(
13268        "
13269            ˇaaa
13270          - bbb
13271          + BBB
13272
13273          + EEE
13274            fff
13275        "
13276        .unindent(),
13277    );
13278}
13279
13280#[gpui::test]
13281async fn test_edits_around_expanded_insertion_hunks(
13282    executor: BackgroundExecutor,
13283    cx: &mut gpui::TestAppContext,
13284) {
13285    init_test(cx, |_| {});
13286
13287    let mut cx = EditorTestContext::new(cx).await;
13288
13289    let diff_base = r#"
13290        use some::mod1;
13291        use some::mod2;
13292
13293        const A: u32 = 42;
13294
13295        fn main() {
13296            println!("hello");
13297
13298            println!("world");
13299        }
13300        "#
13301    .unindent();
13302    executor.run_until_parked();
13303    cx.set_state(
13304        &r#"
13305        use some::mod1;
13306        use some::mod2;
13307
13308        const A: u32 = 42;
13309        const B: u32 = 42;
13310        const C: u32 = 42;
13311        ˇ
13312
13313        fn main() {
13314            println!("hello");
13315
13316            println!("world");
13317        }
13318        "#
13319        .unindent(),
13320    );
13321
13322    cx.set_diff_base(&diff_base);
13323    executor.run_until_parked();
13324
13325    cx.update_editor(|editor, window, cx| {
13326        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13327    });
13328    executor.run_until_parked();
13329
13330    cx.assert_state_with_diff(
13331        r#"
13332        use some::mod1;
13333        use some::mod2;
13334
13335        const A: u32 = 42;
13336      + const B: u32 = 42;
13337      + const C: u32 = 42;
13338      + ˇ
13339
13340        fn main() {
13341            println!("hello");
13342
13343            println!("world");
13344        }
13345      "#
13346        .unindent(),
13347    );
13348
13349    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13350    executor.run_until_parked();
13351
13352    cx.assert_state_with_diff(
13353        r#"
13354        use some::mod1;
13355        use some::mod2;
13356
13357        const A: u32 = 42;
13358      + const B: u32 = 42;
13359      + const C: u32 = 42;
13360      + const D: u32 = 42;
13361      + ˇ
13362
13363        fn main() {
13364            println!("hello");
13365
13366            println!("world");
13367        }
13368      "#
13369        .unindent(),
13370    );
13371
13372    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13373    executor.run_until_parked();
13374
13375    cx.assert_state_with_diff(
13376        r#"
13377        use some::mod1;
13378        use some::mod2;
13379
13380        const A: u32 = 42;
13381      + const B: u32 = 42;
13382      + const C: u32 = 42;
13383      + const D: u32 = 42;
13384      + const E: u32 = 42;
13385      + ˇ
13386
13387        fn main() {
13388            println!("hello");
13389
13390            println!("world");
13391        }
13392      "#
13393        .unindent(),
13394    );
13395
13396    cx.update_editor(|editor, window, cx| {
13397        editor.delete_line(&DeleteLine, window, cx);
13398    });
13399    executor.run_until_parked();
13400
13401    cx.assert_state_with_diff(
13402        r#"
13403        use some::mod1;
13404        use some::mod2;
13405
13406        const A: u32 = 42;
13407      + const B: u32 = 42;
13408      + const C: u32 = 42;
13409      + const D: u32 = 42;
13410      + const E: u32 = 42;
13411        ˇ
13412        fn main() {
13413            println!("hello");
13414
13415            println!("world");
13416        }
13417      "#
13418        .unindent(),
13419    );
13420
13421    cx.update_editor(|editor, window, cx| {
13422        editor.move_up(&MoveUp, window, cx);
13423        editor.delete_line(&DeleteLine, window, cx);
13424        editor.move_up(&MoveUp, window, cx);
13425        editor.delete_line(&DeleteLine, window, cx);
13426        editor.move_up(&MoveUp, window, cx);
13427        editor.delete_line(&DeleteLine, window, cx);
13428    });
13429    executor.run_until_parked();
13430    cx.assert_state_with_diff(
13431        r#"
13432        use some::mod1;
13433        use some::mod2;
13434
13435        const A: u32 = 42;
13436      + const B: u32 = 42;
13437        ˇ
13438        fn main() {
13439            println!("hello");
13440
13441            println!("world");
13442        }
13443      "#
13444        .unindent(),
13445    );
13446
13447    cx.update_editor(|editor, window, cx| {
13448        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13449        editor.delete_line(&DeleteLine, window, cx);
13450    });
13451    executor.run_until_parked();
13452    cx.assert_state_with_diff(
13453        r#"
13454        ˇ
13455        fn main() {
13456            println!("hello");
13457
13458            println!("world");
13459        }
13460      "#
13461        .unindent(),
13462    );
13463}
13464
13465#[gpui::test]
13466async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13467    init_test(cx, |_| {});
13468
13469    let mut cx = EditorTestContext::new(cx).await;
13470    cx.set_diff_base(indoc! { "
13471        one
13472        two
13473        three
13474        four
13475        five
13476        "
13477    });
13478    cx.set_state(indoc! { "
13479        one
13480        ˇthree
13481        five
13482    "});
13483    cx.run_until_parked();
13484    cx.update_editor(|editor, window, cx| {
13485        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13486    });
13487    cx.assert_state_with_diff(
13488        indoc! { "
13489        one
13490      - two
13491        ˇthree
13492      - four
13493        five
13494    "}
13495        .to_string(),
13496    );
13497    cx.update_editor(|editor, window, cx| {
13498        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13499    });
13500
13501    cx.assert_state_with_diff(
13502        indoc! { "
13503        one
13504        ˇthree
13505        five
13506    "}
13507        .to_string(),
13508    );
13509
13510    cx.set_state(indoc! { "
13511        one
13512        TWO
13513        ˇthree
13514        four
13515        five
13516    "});
13517    cx.run_until_parked();
13518    cx.update_editor(|editor, window, cx| {
13519        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13520    });
13521
13522    cx.assert_state_with_diff(
13523        indoc! { "
13524            one
13525          - two
13526          + TWO
13527            ˇthree
13528            four
13529            five
13530        "}
13531        .to_string(),
13532    );
13533    cx.update_editor(|editor, window, cx| {
13534        editor.move_up(&Default::default(), window, cx);
13535        editor.move_up(&Default::default(), window, cx);
13536        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13537    });
13538    cx.assert_state_with_diff(
13539        indoc! { "
13540            one
13541            ˇTWO
13542            three
13543            four
13544            five
13545        "}
13546        .to_string(),
13547    );
13548}
13549
13550#[gpui::test]
13551async fn test_edits_around_expanded_deletion_hunks(
13552    executor: BackgroundExecutor,
13553    cx: &mut gpui::TestAppContext,
13554) {
13555    init_test(cx, |_| {});
13556
13557    let mut cx = EditorTestContext::new(cx).await;
13558
13559    let diff_base = r#"
13560        use some::mod1;
13561        use some::mod2;
13562
13563        const A: u32 = 42;
13564        const B: u32 = 42;
13565        const C: u32 = 42;
13566
13567
13568        fn main() {
13569            println!("hello");
13570
13571            println!("world");
13572        }
13573    "#
13574    .unindent();
13575    executor.run_until_parked();
13576    cx.set_state(
13577        &r#"
13578        use some::mod1;
13579        use some::mod2;
13580
13581        ˇconst B: u32 = 42;
13582        const C: u32 = 42;
13583
13584
13585        fn main() {
13586            println!("hello");
13587
13588            println!("world");
13589        }
13590        "#
13591        .unindent(),
13592    );
13593
13594    cx.set_diff_base(&diff_base);
13595    executor.run_until_parked();
13596
13597    cx.update_editor(|editor, window, cx| {
13598        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13599    });
13600    executor.run_until_parked();
13601
13602    cx.assert_state_with_diff(
13603        r#"
13604        use some::mod1;
13605        use some::mod2;
13606
13607      - const A: u32 = 42;
13608        ˇconst B: u32 = 42;
13609        const C: u32 = 42;
13610
13611
13612        fn main() {
13613            println!("hello");
13614
13615            println!("world");
13616        }
13617      "#
13618        .unindent(),
13619    );
13620
13621    cx.update_editor(|editor, window, cx| {
13622        editor.delete_line(&DeleteLine, window, cx);
13623    });
13624    executor.run_until_parked();
13625    cx.assert_state_with_diff(
13626        r#"
13627        use some::mod1;
13628        use some::mod2;
13629
13630      - const A: u32 = 42;
13631      - const B: u32 = 42;
13632        ˇconst C: u32 = 42;
13633
13634
13635        fn main() {
13636            println!("hello");
13637
13638            println!("world");
13639        }
13640      "#
13641        .unindent(),
13642    );
13643
13644    cx.update_editor(|editor, window, cx| {
13645        editor.delete_line(&DeleteLine, window, cx);
13646    });
13647    executor.run_until_parked();
13648    cx.assert_state_with_diff(
13649        r#"
13650        use some::mod1;
13651        use some::mod2;
13652
13653      - const A: u32 = 42;
13654      - const B: u32 = 42;
13655      - const C: u32 = 42;
13656        ˇ
13657
13658        fn main() {
13659            println!("hello");
13660
13661            println!("world");
13662        }
13663      "#
13664        .unindent(),
13665    );
13666
13667    cx.update_editor(|editor, window, cx| {
13668        editor.handle_input("replacement", window, cx);
13669    });
13670    executor.run_until_parked();
13671    cx.assert_state_with_diff(
13672        r#"
13673        use some::mod1;
13674        use some::mod2;
13675
13676      - const A: u32 = 42;
13677      - const B: u32 = 42;
13678      - const C: u32 = 42;
13679      -
13680      + replacementˇ
13681
13682        fn main() {
13683            println!("hello");
13684
13685            println!("world");
13686        }
13687      "#
13688        .unindent(),
13689    );
13690}
13691
13692#[gpui::test]
13693async fn test_backspace_after_deletion_hunk(
13694    executor: BackgroundExecutor,
13695    cx: &mut gpui::TestAppContext,
13696) {
13697    init_test(cx, |_| {});
13698
13699    let mut cx = EditorTestContext::new(cx).await;
13700
13701    let base_text = r#"
13702        one
13703        two
13704        three
13705        four
13706        five
13707    "#
13708    .unindent();
13709    executor.run_until_parked();
13710    cx.set_state(
13711        &r#"
13712        one
13713        two
13714        fˇour
13715        five
13716        "#
13717        .unindent(),
13718    );
13719
13720    cx.set_diff_base(&base_text);
13721    executor.run_until_parked();
13722
13723    cx.update_editor(|editor, window, cx| {
13724        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13725    });
13726    executor.run_until_parked();
13727
13728    cx.assert_state_with_diff(
13729        r#"
13730          one
13731          two
13732        - three
13733          fˇour
13734          five
13735        "#
13736        .unindent(),
13737    );
13738
13739    cx.update_editor(|editor, window, cx| {
13740        editor.backspace(&Backspace, window, cx);
13741        editor.backspace(&Backspace, window, cx);
13742    });
13743    executor.run_until_parked();
13744    cx.assert_state_with_diff(
13745        r#"
13746          one
13747          two
13748        - threeˇ
13749        - four
13750        + our
13751          five
13752        "#
13753        .unindent(),
13754    );
13755}
13756
13757#[gpui::test]
13758async fn test_edit_after_expanded_modification_hunk(
13759    executor: BackgroundExecutor,
13760    cx: &mut gpui::TestAppContext,
13761) {
13762    init_test(cx, |_| {});
13763
13764    let mut cx = EditorTestContext::new(cx).await;
13765
13766    let diff_base = r#"
13767        use some::mod1;
13768        use some::mod2;
13769
13770        const A: u32 = 42;
13771        const B: u32 = 42;
13772        const C: u32 = 42;
13773        const D: u32 = 42;
13774
13775
13776        fn main() {
13777            println!("hello");
13778
13779            println!("world");
13780        }"#
13781    .unindent();
13782
13783    cx.set_state(
13784        &r#"
13785        use some::mod1;
13786        use some::mod2;
13787
13788        const A: u32 = 42;
13789        const B: u32 = 42;
13790        const C: u32 = 43ˇ
13791        const D: u32 = 42;
13792
13793
13794        fn main() {
13795            println!("hello");
13796
13797            println!("world");
13798        }"#
13799        .unindent(),
13800    );
13801
13802    cx.set_diff_base(&diff_base);
13803    executor.run_until_parked();
13804    cx.update_editor(|editor, window, cx| {
13805        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13806    });
13807    executor.run_until_parked();
13808
13809    cx.assert_state_with_diff(
13810        r#"
13811        use some::mod1;
13812        use some::mod2;
13813
13814        const A: u32 = 42;
13815        const B: u32 = 42;
13816      - const C: u32 = 42;
13817      + const C: u32 = 43ˇ
13818        const D: u32 = 42;
13819
13820
13821        fn main() {
13822            println!("hello");
13823
13824            println!("world");
13825        }"#
13826        .unindent(),
13827    );
13828
13829    cx.update_editor(|editor, window, cx| {
13830        editor.handle_input("\nnew_line\n", window, cx);
13831    });
13832    executor.run_until_parked();
13833
13834    cx.assert_state_with_diff(
13835        r#"
13836        use some::mod1;
13837        use some::mod2;
13838
13839        const A: u32 = 42;
13840        const B: u32 = 42;
13841      - const C: u32 = 42;
13842      + const C: u32 = 43
13843      + new_line
13844      + ˇ
13845        const D: u32 = 42;
13846
13847
13848        fn main() {
13849            println!("hello");
13850
13851            println!("world");
13852        }"#
13853        .unindent(),
13854    );
13855}
13856
13857async fn setup_indent_guides_editor(
13858    text: &str,
13859    cx: &mut gpui::TestAppContext,
13860) -> (BufferId, EditorTestContext) {
13861    init_test(cx, |_| {});
13862
13863    let mut cx = EditorTestContext::new(cx).await;
13864
13865    let buffer_id = cx.update_editor(|editor, window, cx| {
13866        editor.set_text(text, window, cx);
13867        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13868
13869        buffer_ids[0]
13870    });
13871
13872    (buffer_id, cx)
13873}
13874
13875fn assert_indent_guides(
13876    range: Range<u32>,
13877    expected: Vec<IndentGuide>,
13878    active_indices: Option<Vec<usize>>,
13879    cx: &mut EditorTestContext,
13880) {
13881    let indent_guides = cx.update_editor(|editor, window, cx| {
13882        let snapshot = editor.snapshot(window, cx).display_snapshot;
13883        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13884            editor,
13885            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13886            true,
13887            &snapshot,
13888            cx,
13889        );
13890
13891        indent_guides.sort_by(|a, b| {
13892            a.depth.cmp(&b.depth).then(
13893                a.start_row
13894                    .cmp(&b.start_row)
13895                    .then(a.end_row.cmp(&b.end_row)),
13896            )
13897        });
13898        indent_guides
13899    });
13900
13901    if let Some(expected) = active_indices {
13902        let active_indices = cx.update_editor(|editor, window, cx| {
13903            let snapshot = editor.snapshot(window, cx).display_snapshot;
13904            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13905        });
13906
13907        assert_eq!(
13908            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13909            expected,
13910            "Active indent guide indices do not match"
13911        );
13912    }
13913
13914    assert_eq!(indent_guides, expected, "Indent guides do not match");
13915}
13916
13917fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13918    IndentGuide {
13919        buffer_id,
13920        start_row: MultiBufferRow(start_row),
13921        end_row: MultiBufferRow(end_row),
13922        depth,
13923        tab_size: 4,
13924        settings: IndentGuideSettings {
13925            enabled: true,
13926            line_width: 1,
13927            active_line_width: 1,
13928            ..Default::default()
13929        },
13930    }
13931}
13932
13933#[gpui::test]
13934async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13935    let (buffer_id, mut cx) = setup_indent_guides_editor(
13936        &"
13937    fn main() {
13938        let a = 1;
13939    }"
13940        .unindent(),
13941        cx,
13942    )
13943    .await;
13944
13945    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13946}
13947
13948#[gpui::test]
13949async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13950    let (buffer_id, mut cx) = setup_indent_guides_editor(
13951        &"
13952    fn main() {
13953        let a = 1;
13954        let b = 2;
13955    }"
13956        .unindent(),
13957        cx,
13958    )
13959    .await;
13960
13961    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13962}
13963
13964#[gpui::test]
13965async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13966    let (buffer_id, mut cx) = setup_indent_guides_editor(
13967        &"
13968    fn main() {
13969        let a = 1;
13970        if a == 3 {
13971            let b = 2;
13972        } else {
13973            let c = 3;
13974        }
13975    }"
13976        .unindent(),
13977        cx,
13978    )
13979    .await;
13980
13981    assert_indent_guides(
13982        0..8,
13983        vec![
13984            indent_guide(buffer_id, 1, 6, 0),
13985            indent_guide(buffer_id, 3, 3, 1),
13986            indent_guide(buffer_id, 5, 5, 1),
13987        ],
13988        None,
13989        &mut cx,
13990    );
13991}
13992
13993#[gpui::test]
13994async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13995    let (buffer_id, mut cx) = setup_indent_guides_editor(
13996        &"
13997    fn main() {
13998        let a = 1;
13999            let b = 2;
14000        let c = 3;
14001    }"
14002        .unindent(),
14003        cx,
14004    )
14005    .await;
14006
14007    assert_indent_guides(
14008        0..5,
14009        vec![
14010            indent_guide(buffer_id, 1, 3, 0),
14011            indent_guide(buffer_id, 2, 2, 1),
14012        ],
14013        None,
14014        &mut cx,
14015    );
14016}
14017
14018#[gpui::test]
14019async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14020    let (buffer_id, mut cx) = setup_indent_guides_editor(
14021        &"
14022        fn main() {
14023            let a = 1;
14024
14025            let c = 3;
14026        }"
14027        .unindent(),
14028        cx,
14029    )
14030    .await;
14031
14032    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14033}
14034
14035#[gpui::test]
14036async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14037    let (buffer_id, mut cx) = setup_indent_guides_editor(
14038        &"
14039        fn main() {
14040            let a = 1;
14041
14042            let c = 3;
14043
14044            if a == 3 {
14045                let b = 2;
14046            } else {
14047                let c = 3;
14048            }
14049        }"
14050        .unindent(),
14051        cx,
14052    )
14053    .await;
14054
14055    assert_indent_guides(
14056        0..11,
14057        vec![
14058            indent_guide(buffer_id, 1, 9, 0),
14059            indent_guide(buffer_id, 6, 6, 1),
14060            indent_guide(buffer_id, 8, 8, 1),
14061        ],
14062        None,
14063        &mut cx,
14064    );
14065}
14066
14067#[gpui::test]
14068async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14069    let (buffer_id, mut cx) = setup_indent_guides_editor(
14070        &"
14071        fn main() {
14072            let a = 1;
14073
14074            let c = 3;
14075
14076            if a == 3 {
14077                let b = 2;
14078            } else {
14079                let c = 3;
14080            }
14081        }"
14082        .unindent(),
14083        cx,
14084    )
14085    .await;
14086
14087    assert_indent_guides(
14088        1..11,
14089        vec![
14090            indent_guide(buffer_id, 1, 9, 0),
14091            indent_guide(buffer_id, 6, 6, 1),
14092            indent_guide(buffer_id, 8, 8, 1),
14093        ],
14094        None,
14095        &mut cx,
14096    );
14097}
14098
14099#[gpui::test]
14100async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14101    let (buffer_id, mut cx) = setup_indent_guides_editor(
14102        &"
14103        fn main() {
14104            let a = 1;
14105
14106            let c = 3;
14107
14108            if a == 3 {
14109                let b = 2;
14110            } else {
14111                let c = 3;
14112            }
14113        }"
14114        .unindent(),
14115        cx,
14116    )
14117    .await;
14118
14119    assert_indent_guides(
14120        1..10,
14121        vec![
14122            indent_guide(buffer_id, 1, 9, 0),
14123            indent_guide(buffer_id, 6, 6, 1),
14124            indent_guide(buffer_id, 8, 8, 1),
14125        ],
14126        None,
14127        &mut cx,
14128    );
14129}
14130
14131#[gpui::test]
14132async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14133    let (buffer_id, mut cx) = setup_indent_guides_editor(
14134        &"
14135        block1
14136            block2
14137                block3
14138                    block4
14139            block2
14140        block1
14141        block1"
14142            .unindent(),
14143        cx,
14144    )
14145    .await;
14146
14147    assert_indent_guides(
14148        1..10,
14149        vec![
14150            indent_guide(buffer_id, 1, 4, 0),
14151            indent_guide(buffer_id, 2, 3, 1),
14152            indent_guide(buffer_id, 3, 3, 2),
14153        ],
14154        None,
14155        &mut cx,
14156    );
14157}
14158
14159#[gpui::test]
14160async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14161    let (buffer_id, mut cx) = setup_indent_guides_editor(
14162        &"
14163        block1
14164            block2
14165                block3
14166
14167        block1
14168        block1"
14169            .unindent(),
14170        cx,
14171    )
14172    .await;
14173
14174    assert_indent_guides(
14175        0..6,
14176        vec![
14177            indent_guide(buffer_id, 1, 2, 0),
14178            indent_guide(buffer_id, 2, 2, 1),
14179        ],
14180        None,
14181        &mut cx,
14182    );
14183}
14184
14185#[gpui::test]
14186async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14187    let (buffer_id, mut cx) = setup_indent_guides_editor(
14188        &"
14189        block1
14190
14191
14192
14193            block2
14194        "
14195        .unindent(),
14196        cx,
14197    )
14198    .await;
14199
14200    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14201}
14202
14203#[gpui::test]
14204async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14205    let (buffer_id, mut cx) = setup_indent_guides_editor(
14206        &"
14207        def a:
14208        \tb = 3
14209        \tif True:
14210        \t\tc = 4
14211        \t\td = 5
14212        \tprint(b)
14213        "
14214        .unindent(),
14215        cx,
14216    )
14217    .await;
14218
14219    assert_indent_guides(
14220        0..6,
14221        vec![
14222            indent_guide(buffer_id, 1, 6, 0),
14223            indent_guide(buffer_id, 3, 4, 1),
14224        ],
14225        None,
14226        &mut cx,
14227    );
14228}
14229
14230#[gpui::test]
14231async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14232    let (buffer_id, mut cx) = setup_indent_guides_editor(
14233        &"
14234    fn main() {
14235        let a = 1;
14236    }"
14237        .unindent(),
14238        cx,
14239    )
14240    .await;
14241
14242    cx.update_editor(|editor, window, cx| {
14243        editor.change_selections(None, window, cx, |s| {
14244            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14245        });
14246    });
14247
14248    assert_indent_guides(
14249        0..3,
14250        vec![indent_guide(buffer_id, 1, 1, 0)],
14251        Some(vec![0]),
14252        &mut cx,
14253    );
14254}
14255
14256#[gpui::test]
14257async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14258    let (buffer_id, mut cx) = setup_indent_guides_editor(
14259        &"
14260    fn main() {
14261        if 1 == 2 {
14262            let a = 1;
14263        }
14264    }"
14265        .unindent(),
14266        cx,
14267    )
14268    .await;
14269
14270    cx.update_editor(|editor, window, cx| {
14271        editor.change_selections(None, window, cx, |s| {
14272            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14273        });
14274    });
14275
14276    assert_indent_guides(
14277        0..4,
14278        vec![
14279            indent_guide(buffer_id, 1, 3, 0),
14280            indent_guide(buffer_id, 2, 2, 1),
14281        ],
14282        Some(vec![1]),
14283        &mut cx,
14284    );
14285
14286    cx.update_editor(|editor, window, cx| {
14287        editor.change_selections(None, window, cx, |s| {
14288            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14289        });
14290    });
14291
14292    assert_indent_guides(
14293        0..4,
14294        vec![
14295            indent_guide(buffer_id, 1, 3, 0),
14296            indent_guide(buffer_id, 2, 2, 1),
14297        ],
14298        Some(vec![1]),
14299        &mut cx,
14300    );
14301
14302    cx.update_editor(|editor, window, cx| {
14303        editor.change_selections(None, window, cx, |s| {
14304            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14305        });
14306    });
14307
14308    assert_indent_guides(
14309        0..4,
14310        vec![
14311            indent_guide(buffer_id, 1, 3, 0),
14312            indent_guide(buffer_id, 2, 2, 1),
14313        ],
14314        Some(vec![0]),
14315        &mut cx,
14316    );
14317}
14318
14319#[gpui::test]
14320async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14321    let (buffer_id, mut cx) = setup_indent_guides_editor(
14322        &"
14323    fn main() {
14324        let a = 1;
14325
14326        let b = 2;
14327    }"
14328        .unindent(),
14329        cx,
14330    )
14331    .await;
14332
14333    cx.update_editor(|editor, window, cx| {
14334        editor.change_selections(None, window, cx, |s| {
14335            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14336        });
14337    });
14338
14339    assert_indent_guides(
14340        0..5,
14341        vec![indent_guide(buffer_id, 1, 3, 0)],
14342        Some(vec![0]),
14343        &mut cx,
14344    );
14345}
14346
14347#[gpui::test]
14348async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14349    let (buffer_id, mut cx) = setup_indent_guides_editor(
14350        &"
14351    def m:
14352        a = 1
14353        pass"
14354            .unindent(),
14355        cx,
14356    )
14357    .await;
14358
14359    cx.update_editor(|editor, window, cx| {
14360        editor.change_selections(None, window, cx, |s| {
14361            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14362        });
14363    });
14364
14365    assert_indent_guides(
14366        0..3,
14367        vec![indent_guide(buffer_id, 1, 2, 0)],
14368        Some(vec![0]),
14369        &mut cx,
14370    );
14371}
14372
14373#[gpui::test]
14374async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14375    init_test(cx, |_| {});
14376    let mut cx = EditorTestContext::new(cx).await;
14377    let text = indoc! {
14378        "
14379        impl A {
14380            fn b() {
14381                0;
14382                3;
14383                5;
14384                6;
14385                7;
14386            }
14387        }
14388        "
14389    };
14390    let base_text = indoc! {
14391        "
14392        impl A {
14393            fn b() {
14394                0;
14395                1;
14396                2;
14397                3;
14398                4;
14399            }
14400            fn c() {
14401                5;
14402                6;
14403                7;
14404            }
14405        }
14406        "
14407    };
14408
14409    cx.update_editor(|editor, window, cx| {
14410        editor.set_text(text, window, cx);
14411
14412        editor.buffer().update(cx, |multibuffer, cx| {
14413            let buffer = multibuffer.as_singleton().unwrap();
14414            let change_set = cx.new(|cx| {
14415                let mut change_set = BufferChangeSet::new(&buffer, cx);
14416                let _ =
14417                    change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
14418                change_set
14419            });
14420
14421            multibuffer.set_all_diff_hunks_expanded(cx);
14422            multibuffer.add_change_set(change_set, cx);
14423
14424            buffer.read(cx).remote_id()
14425        })
14426    });
14427    cx.run_until_parked();
14428
14429    cx.assert_state_with_diff(
14430        indoc! { "
14431          impl A {
14432              fn b() {
14433                  0;
14434        -         1;
14435        -         2;
14436                  3;
14437        -         4;
14438        -     }
14439        -     fn c() {
14440                  5;
14441                  6;
14442                  7;
14443              }
14444          }
14445          ˇ"
14446        }
14447        .to_string(),
14448    );
14449
14450    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14451        editor
14452            .snapshot(window, cx)
14453            .buffer_snapshot
14454            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14455            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14456            .collect::<Vec<_>>()
14457    });
14458    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14459    assert_eq!(
14460        actual_guides,
14461        vec![
14462            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14463            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14464            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14465        ]
14466    );
14467}
14468
14469#[gpui::test]
14470fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14471    init_test(cx, |_| {});
14472
14473    let editor = cx.add_window(|window, cx| {
14474        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14475        build_editor(buffer, window, cx)
14476    });
14477
14478    let render_args = Arc::new(Mutex::new(None));
14479    let snapshot = editor
14480        .update(cx, |editor, window, cx| {
14481            let snapshot = editor.buffer().read(cx).snapshot(cx);
14482            let range =
14483                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14484
14485            struct RenderArgs {
14486                row: MultiBufferRow,
14487                folded: bool,
14488                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14489            }
14490
14491            let crease = Crease::inline(
14492                range,
14493                FoldPlaceholder::test(),
14494                {
14495                    let toggle_callback = render_args.clone();
14496                    move |row, folded, callback, _window, _cx| {
14497                        *toggle_callback.lock() = Some(RenderArgs {
14498                            row,
14499                            folded,
14500                            callback,
14501                        });
14502                        div()
14503                    }
14504                },
14505                |_row, _folded, _window, _cx| div(),
14506            );
14507
14508            editor.insert_creases(Some(crease), cx);
14509            let snapshot = editor.snapshot(window, cx);
14510            let _div = snapshot.render_crease_toggle(
14511                MultiBufferRow(1),
14512                false,
14513                cx.entity().clone(),
14514                window,
14515                cx,
14516            );
14517            snapshot
14518        })
14519        .unwrap();
14520
14521    let render_args = render_args.lock().take().unwrap();
14522    assert_eq!(render_args.row, MultiBufferRow(1));
14523    assert!(!render_args.folded);
14524    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14525
14526    cx.update_window(*editor, |_, window, cx| {
14527        (render_args.callback)(true, 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    cx.update_window(*editor, |_, window, cx| {
14536        (render_args.callback)(false, window, cx)
14537    })
14538    .unwrap();
14539    let snapshot = editor
14540        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14541        .unwrap();
14542    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14543}
14544
14545#[gpui::test]
14546async fn test_input_text(cx: &mut gpui::TestAppContext) {
14547    init_test(cx, |_| {});
14548    let mut cx = EditorTestContext::new(cx).await;
14549
14550    cx.set_state(
14551        &r#"ˇone
14552        two
14553
14554        three
14555        fourˇ
14556        five
14557
14558        siˇx"#
14559            .unindent(),
14560    );
14561
14562    cx.dispatch_action(HandleInput(String::new()));
14563    cx.assert_editor_state(
14564        &r#"ˇone
14565        two
14566
14567        three
14568        fourˇ
14569        five
14570
14571        siˇx"#
14572            .unindent(),
14573    );
14574
14575    cx.dispatch_action(HandleInput("AAAA".to_string()));
14576    cx.assert_editor_state(
14577        &r#"AAAAˇone
14578        two
14579
14580        three
14581        fourAAAAˇ
14582        five
14583
14584        siAAAAˇx"#
14585            .unindent(),
14586    );
14587}
14588
14589#[gpui::test]
14590async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14591    init_test(cx, |_| {});
14592
14593    let mut cx = EditorTestContext::new(cx).await;
14594    cx.set_state(
14595        r#"let foo = 1;
14596let foo = 2;
14597let foo = 3;
14598let fooˇ = 4;
14599let foo = 5;
14600let foo = 6;
14601let foo = 7;
14602let foo = 8;
14603let foo = 9;
14604let foo = 10;
14605let foo = 11;
14606let foo = 12;
14607let foo = 13;
14608let foo = 14;
14609let foo = 15;"#,
14610    );
14611
14612    cx.update_editor(|e, window, cx| {
14613        assert_eq!(
14614            e.next_scroll_position,
14615            NextScrollCursorCenterTopBottom::Center,
14616            "Default next scroll direction is center",
14617        );
14618
14619        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14620        assert_eq!(
14621            e.next_scroll_position,
14622            NextScrollCursorCenterTopBottom::Top,
14623            "After center, next scroll direction should be top",
14624        );
14625
14626        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14627        assert_eq!(
14628            e.next_scroll_position,
14629            NextScrollCursorCenterTopBottom::Bottom,
14630            "After top, next scroll direction should be bottom",
14631        );
14632
14633        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14634        assert_eq!(
14635            e.next_scroll_position,
14636            NextScrollCursorCenterTopBottom::Center,
14637            "After bottom, scrolling should start over",
14638        );
14639
14640        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14641        assert_eq!(
14642            e.next_scroll_position,
14643            NextScrollCursorCenterTopBottom::Top,
14644            "Scrolling continues if retriggered fast enough"
14645        );
14646    });
14647
14648    cx.executor()
14649        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14650    cx.executor().run_until_parked();
14651    cx.update_editor(|e, _, _| {
14652        assert_eq!(
14653            e.next_scroll_position,
14654            NextScrollCursorCenterTopBottom::Center,
14655            "If scrolling is not triggered fast enough, it should reset"
14656        );
14657    });
14658}
14659
14660#[gpui::test]
14661async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14662    init_test(cx, |_| {});
14663    let mut cx = EditorLspTestContext::new_rust(
14664        lsp::ServerCapabilities {
14665            definition_provider: Some(lsp::OneOf::Left(true)),
14666            references_provider: Some(lsp::OneOf::Left(true)),
14667            ..lsp::ServerCapabilities::default()
14668        },
14669        cx,
14670    )
14671    .await;
14672
14673    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14674        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14675            move |params, _| async move {
14676                if empty_go_to_definition {
14677                    Ok(None)
14678                } else {
14679                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14680                        uri: params.text_document_position_params.text_document.uri,
14681                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14682                    })))
14683                }
14684            },
14685        );
14686        let references =
14687            cx.lsp
14688                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14689                    Ok(Some(vec![lsp::Location {
14690                        uri: params.text_document_position.text_document.uri,
14691                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14692                    }]))
14693                });
14694        (go_to_definition, references)
14695    };
14696
14697    cx.set_state(
14698        &r#"fn one() {
14699            let mut a = ˇtwo();
14700        }
14701
14702        fn two() {}"#
14703            .unindent(),
14704    );
14705    set_up_lsp_handlers(false, &mut cx);
14706    let navigated = cx
14707        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14708        .await
14709        .expect("Failed to navigate to definition");
14710    assert_eq!(
14711        navigated,
14712        Navigated::Yes,
14713        "Should have navigated to definition from the GetDefinition response"
14714    );
14715    cx.assert_editor_state(
14716        &r#"fn one() {
14717            let mut a = two();
14718        }
14719
14720        fn «twoˇ»() {}"#
14721            .unindent(),
14722    );
14723
14724    let editors = cx.update_workspace(|workspace, _, cx| {
14725        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14726    });
14727    cx.update_editor(|_, _, test_editor_cx| {
14728        assert_eq!(
14729            editors.len(),
14730            1,
14731            "Initially, only one, test, editor should be open in the workspace"
14732        );
14733        assert_eq!(
14734            test_editor_cx.entity(),
14735            editors.last().expect("Asserted len is 1").clone()
14736        );
14737    });
14738
14739    set_up_lsp_handlers(true, &mut cx);
14740    let navigated = cx
14741        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14742        .await
14743        .expect("Failed to navigate to lookup references");
14744    assert_eq!(
14745        navigated,
14746        Navigated::Yes,
14747        "Should have navigated to references as a fallback after empty GoToDefinition response"
14748    );
14749    // We should not change the selections in the existing file,
14750    // if opening another milti buffer with the references
14751    cx.assert_editor_state(
14752        &r#"fn one() {
14753            let mut a = two();
14754        }
14755
14756        fn «twoˇ»() {}"#
14757            .unindent(),
14758    );
14759    let editors = cx.update_workspace(|workspace, _, cx| {
14760        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14761    });
14762    cx.update_editor(|_, _, test_editor_cx| {
14763        assert_eq!(
14764            editors.len(),
14765            2,
14766            "After falling back to references search, we open a new editor with the results"
14767        );
14768        let references_fallback_text = editors
14769            .into_iter()
14770            .find(|new_editor| *new_editor != test_editor_cx.entity())
14771            .expect("Should have one non-test editor now")
14772            .read(test_editor_cx)
14773            .text(test_editor_cx);
14774        assert_eq!(
14775            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14776            "Should use the range from the references response and not the GoToDefinition one"
14777        );
14778    });
14779}
14780
14781#[gpui::test]
14782async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14783    init_test(cx, |_| {});
14784
14785    let language = Arc::new(Language::new(
14786        LanguageConfig::default(),
14787        Some(tree_sitter_rust::LANGUAGE.into()),
14788    ));
14789
14790    let text = r#"
14791        #[cfg(test)]
14792        mod tests() {
14793            #[test]
14794            fn runnable_1() {
14795                let a = 1;
14796            }
14797
14798            #[test]
14799            fn runnable_2() {
14800                let a = 1;
14801                let b = 2;
14802            }
14803        }
14804    "#
14805    .unindent();
14806
14807    let fs = FakeFs::new(cx.executor());
14808    fs.insert_file("/file.rs", Default::default()).await;
14809
14810    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14811    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14812    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14813    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14814    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14815
14816    let editor = cx.new_window_entity(|window, cx| {
14817        Editor::new(
14818            EditorMode::Full,
14819            multi_buffer,
14820            Some(project.clone()),
14821            true,
14822            window,
14823            cx,
14824        )
14825    });
14826
14827    editor.update_in(cx, |editor, window, cx| {
14828        editor.tasks.insert(
14829            (buffer.read(cx).remote_id(), 3),
14830            RunnableTasks {
14831                templates: vec![],
14832                offset: MultiBufferOffset(43),
14833                column: 0,
14834                extra_variables: HashMap::default(),
14835                context_range: BufferOffset(43)..BufferOffset(85),
14836            },
14837        );
14838        editor.tasks.insert(
14839            (buffer.read(cx).remote_id(), 8),
14840            RunnableTasks {
14841                templates: vec![],
14842                offset: MultiBufferOffset(86),
14843                column: 0,
14844                extra_variables: HashMap::default(),
14845                context_range: BufferOffset(86)..BufferOffset(191),
14846            },
14847        );
14848
14849        // Test finding task when cursor is inside function body
14850        editor.change_selections(None, window, cx, |s| {
14851            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14852        });
14853        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14854        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14855
14856        // Test finding task when cursor is on function name
14857        editor.change_selections(None, window, cx, |s| {
14858            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14859        });
14860        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14861        assert_eq!(row, 8, "Should find task when cursor is on function name");
14862    });
14863}
14864
14865#[gpui::test]
14866async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14867    init_test(cx, |_| {});
14868
14869    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14870    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14871    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14872
14873    let fs = FakeFs::new(cx.executor());
14874    fs.insert_tree(
14875        "/a",
14876        json!({
14877            "first.rs": sample_text_1,
14878            "second.rs": sample_text_2,
14879            "third.rs": sample_text_3,
14880        }),
14881    )
14882    .await;
14883    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14884    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14885    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14886    let worktree = project.update(cx, |project, cx| {
14887        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14888        assert_eq!(worktrees.len(), 1);
14889        worktrees.pop().unwrap()
14890    });
14891    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14892
14893    let buffer_1 = project
14894        .update(cx, |project, cx| {
14895            project.open_buffer((worktree_id, "first.rs"), cx)
14896        })
14897        .await
14898        .unwrap();
14899    let buffer_2 = project
14900        .update(cx, |project, cx| {
14901            project.open_buffer((worktree_id, "second.rs"), cx)
14902        })
14903        .await
14904        .unwrap();
14905    let buffer_3 = project
14906        .update(cx, |project, cx| {
14907            project.open_buffer((worktree_id, "third.rs"), cx)
14908        })
14909        .await
14910        .unwrap();
14911
14912    let multi_buffer = cx.new(|cx| {
14913        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14914        multi_buffer.push_excerpts(
14915            buffer_1.clone(),
14916            [
14917                ExcerptRange {
14918                    context: Point::new(0, 0)..Point::new(3, 0),
14919                    primary: None,
14920                },
14921                ExcerptRange {
14922                    context: Point::new(5, 0)..Point::new(7, 0),
14923                    primary: None,
14924                },
14925                ExcerptRange {
14926                    context: Point::new(9, 0)..Point::new(10, 4),
14927                    primary: None,
14928                },
14929            ],
14930            cx,
14931        );
14932        multi_buffer.push_excerpts(
14933            buffer_2.clone(),
14934            [
14935                ExcerptRange {
14936                    context: Point::new(0, 0)..Point::new(3, 0),
14937                    primary: None,
14938                },
14939                ExcerptRange {
14940                    context: Point::new(5, 0)..Point::new(7, 0),
14941                    primary: None,
14942                },
14943                ExcerptRange {
14944                    context: Point::new(9, 0)..Point::new(10, 4),
14945                    primary: None,
14946                },
14947            ],
14948            cx,
14949        );
14950        multi_buffer.push_excerpts(
14951            buffer_3.clone(),
14952            [
14953                ExcerptRange {
14954                    context: Point::new(0, 0)..Point::new(3, 0),
14955                    primary: None,
14956                },
14957                ExcerptRange {
14958                    context: Point::new(5, 0)..Point::new(7, 0),
14959                    primary: None,
14960                },
14961                ExcerptRange {
14962                    context: Point::new(9, 0)..Point::new(10, 4),
14963                    primary: None,
14964                },
14965            ],
14966            cx,
14967        );
14968        multi_buffer
14969    });
14970    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14971        Editor::new(
14972            EditorMode::Full,
14973            multi_buffer,
14974            Some(project.clone()),
14975            true,
14976            window,
14977            cx,
14978        )
14979    });
14980
14981    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";
14982    assert_eq!(
14983        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14984        full_text,
14985    );
14986
14987    multi_buffer_editor.update(cx, |editor, cx| {
14988        editor.fold_buffer(buffer_1.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\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",
14993        "After folding the first buffer, its text should not be displayed"
14994    );
14995
14996    multi_buffer_editor.update(cx, |editor, cx| {
14997        editor.fold_buffer(buffer_2.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\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15002        "After folding the second buffer, its text should not be displayed"
15003    );
15004
15005    multi_buffer_editor.update(cx, |editor, cx| {
15006        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15007    });
15008    assert_eq!(
15009        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15010        "\n\n\n\n\n",
15011        "After folding the third buffer, its text should not be displayed"
15012    );
15013
15014    // Emulate selection inside the fold logic, that should work
15015    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15016        editor
15017            .snapshot(window, cx)
15018            .next_line_boundary(Point::new(0, 4));
15019    });
15020
15021    multi_buffer_editor.update(cx, |editor, cx| {
15022        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15023    });
15024    assert_eq!(
15025        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15026        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15027        "After unfolding the second buffer, its text should be displayed"
15028    );
15029
15030    multi_buffer_editor.update(cx, |editor, cx| {
15031        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15032    });
15033    assert_eq!(
15034        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15035        "\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",
15036        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15037    );
15038
15039    multi_buffer_editor.update(cx, |editor, cx| {
15040        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15041    });
15042    assert_eq!(
15043        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15044        full_text,
15045        "After unfolding the all buffers, all original text should be displayed"
15046    );
15047}
15048
15049#[gpui::test]
15050async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15051    init_test(cx, |_| {});
15052
15053    let sample_text_1 = "1111\n2222\n3333".to_string();
15054    let sample_text_2 = "4444\n5555\n6666".to_string();
15055    let sample_text_3 = "7777\n8888\n9999".to_string();
15056
15057    let fs = FakeFs::new(cx.executor());
15058    fs.insert_tree(
15059        "/a",
15060        json!({
15061            "first.rs": sample_text_1,
15062            "second.rs": sample_text_2,
15063            "third.rs": sample_text_3,
15064        }),
15065    )
15066    .await;
15067    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15068    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15069    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15070    let worktree = project.update(cx, |project, cx| {
15071        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15072        assert_eq!(worktrees.len(), 1);
15073        worktrees.pop().unwrap()
15074    });
15075    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15076
15077    let buffer_1 = project
15078        .update(cx, |project, cx| {
15079            project.open_buffer((worktree_id, "first.rs"), cx)
15080        })
15081        .await
15082        .unwrap();
15083    let buffer_2 = project
15084        .update(cx, |project, cx| {
15085            project.open_buffer((worktree_id, "second.rs"), cx)
15086        })
15087        .await
15088        .unwrap();
15089    let buffer_3 = project
15090        .update(cx, |project, cx| {
15091            project.open_buffer((worktree_id, "third.rs"), cx)
15092        })
15093        .await
15094        .unwrap();
15095
15096    let multi_buffer = cx.new(|cx| {
15097        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15098        multi_buffer.push_excerpts(
15099            buffer_1.clone(),
15100            [ExcerptRange {
15101                context: Point::new(0, 0)..Point::new(3, 0),
15102                primary: None,
15103            }],
15104            cx,
15105        );
15106        multi_buffer.push_excerpts(
15107            buffer_2.clone(),
15108            [ExcerptRange {
15109                context: Point::new(0, 0)..Point::new(3, 0),
15110                primary: None,
15111            }],
15112            cx,
15113        );
15114        multi_buffer.push_excerpts(
15115            buffer_3.clone(),
15116            [ExcerptRange {
15117                context: Point::new(0, 0)..Point::new(3, 0),
15118                primary: None,
15119            }],
15120            cx,
15121        );
15122        multi_buffer
15123    });
15124
15125    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15126        Editor::new(
15127            EditorMode::Full,
15128            multi_buffer,
15129            Some(project.clone()),
15130            true,
15131            window,
15132            cx,
15133        )
15134    });
15135
15136    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15137    assert_eq!(
15138        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15139        full_text,
15140    );
15141
15142    multi_buffer_editor.update(cx, |editor, cx| {
15143        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15144    });
15145    assert_eq!(
15146        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15147        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15148        "After folding the first buffer, its text should not be displayed"
15149    );
15150
15151    multi_buffer_editor.update(cx, |editor, cx| {
15152        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15153    });
15154
15155    assert_eq!(
15156        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15157        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15158        "After folding the second buffer, its text should not be displayed"
15159    );
15160
15161    multi_buffer_editor.update(cx, |editor, cx| {
15162        editor.fold_buffer(buffer_3.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\n",
15167        "After folding the third buffer, its text should not be displayed"
15168    );
15169
15170    multi_buffer_editor.update(cx, |editor, cx| {
15171        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15172    });
15173    assert_eq!(
15174        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15175        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15176        "After unfolding the second buffer, its text should be displayed"
15177    );
15178
15179    multi_buffer_editor.update(cx, |editor, cx| {
15180        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15181    });
15182    assert_eq!(
15183        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15184        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15185        "After unfolding the first buffer, its text should be displayed"
15186    );
15187
15188    multi_buffer_editor.update(cx, |editor, cx| {
15189        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15190    });
15191    assert_eq!(
15192        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15193        full_text,
15194        "After unfolding all buffers, all original text should be displayed"
15195    );
15196}
15197
15198#[gpui::test]
15199async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15200    init_test(cx, |_| {});
15201
15202    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15203
15204    let fs = FakeFs::new(cx.executor());
15205    fs.insert_tree(
15206        "/a",
15207        json!({
15208            "main.rs": sample_text,
15209        }),
15210    )
15211    .await;
15212    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15213    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15214    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15215    let worktree = project.update(cx, |project, cx| {
15216        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15217        assert_eq!(worktrees.len(), 1);
15218        worktrees.pop().unwrap()
15219    });
15220    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15221
15222    let buffer_1 = project
15223        .update(cx, |project, cx| {
15224            project.open_buffer((worktree_id, "main.rs"), cx)
15225        })
15226        .await
15227        .unwrap();
15228
15229    let multi_buffer = cx.new(|cx| {
15230        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15231        multi_buffer.push_excerpts(
15232            buffer_1.clone(),
15233            [ExcerptRange {
15234                context: Point::new(0, 0)
15235                    ..Point::new(
15236                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15237                        0,
15238                    ),
15239                primary: None,
15240            }],
15241            cx,
15242        );
15243        multi_buffer
15244    });
15245    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15246        Editor::new(
15247            EditorMode::Full,
15248            multi_buffer,
15249            Some(project.clone()),
15250            true,
15251            window,
15252            cx,
15253        )
15254    });
15255
15256    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15257    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15258        enum TestHighlight {}
15259        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15260        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15261        editor.highlight_text::<TestHighlight>(
15262            vec![highlight_range.clone()],
15263            HighlightStyle::color(Hsla::green()),
15264            cx,
15265        );
15266        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15267    });
15268
15269    let full_text = format!("\n\n\n{sample_text}\n");
15270    assert_eq!(
15271        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15272        full_text,
15273    );
15274}
15275
15276#[gpui::test]
15277async fn test_inline_completion_text(cx: &mut TestAppContext) {
15278    init_test(cx, |_| {});
15279
15280    // Simple insertion
15281    assert_highlighted_edits(
15282        "Hello, world!",
15283        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15284        true,
15285        cx,
15286        |highlighted_edits, cx| {
15287            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15288            assert_eq!(highlighted_edits.highlights.len(), 1);
15289            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15290            assert_eq!(
15291                highlighted_edits.highlights[0].1.background_color,
15292                Some(cx.theme().status().created_background)
15293            );
15294        },
15295    )
15296    .await;
15297
15298    // Replacement
15299    assert_highlighted_edits(
15300        "This is a test.",
15301        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15302        false,
15303        cx,
15304        |highlighted_edits, cx| {
15305            assert_eq!(highlighted_edits.text, "That is a test.");
15306            assert_eq!(highlighted_edits.highlights.len(), 1);
15307            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15308            assert_eq!(
15309                highlighted_edits.highlights[0].1.background_color,
15310                Some(cx.theme().status().created_background)
15311            );
15312        },
15313    )
15314    .await;
15315
15316    // Multiple edits
15317    assert_highlighted_edits(
15318        "Hello, world!",
15319        vec![
15320            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15321            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15322        ],
15323        false,
15324        cx,
15325        |highlighted_edits, cx| {
15326            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15327            assert_eq!(highlighted_edits.highlights.len(), 2);
15328            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15329            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15330            assert_eq!(
15331                highlighted_edits.highlights[0].1.background_color,
15332                Some(cx.theme().status().created_background)
15333            );
15334            assert_eq!(
15335                highlighted_edits.highlights[1].1.background_color,
15336                Some(cx.theme().status().created_background)
15337            );
15338        },
15339    )
15340    .await;
15341
15342    // Multiple lines with edits
15343    assert_highlighted_edits(
15344        "First line\nSecond line\nThird line\nFourth line",
15345        vec![
15346            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15347            (
15348                Point::new(2, 0)..Point::new(2, 10),
15349                "New third line".to_string(),
15350            ),
15351            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15352        ],
15353        false,
15354        cx,
15355        |highlighted_edits, cx| {
15356            assert_eq!(
15357                highlighted_edits.text,
15358                "Second modified\nNew third line\nFourth updated line"
15359            );
15360            assert_eq!(highlighted_edits.highlights.len(), 3);
15361            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15362            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15363            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15364            for highlight in &highlighted_edits.highlights {
15365                assert_eq!(
15366                    highlight.1.background_color,
15367                    Some(cx.theme().status().created_background)
15368                );
15369            }
15370        },
15371    )
15372    .await;
15373}
15374
15375#[gpui::test]
15376async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15377    init_test(cx, |_| {});
15378
15379    // Deletion
15380    assert_highlighted_edits(
15381        "Hello, world!",
15382        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15383        true,
15384        cx,
15385        |highlighted_edits, cx| {
15386            assert_eq!(highlighted_edits.text, "Hello, world!");
15387            assert_eq!(highlighted_edits.highlights.len(), 1);
15388            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15389            assert_eq!(
15390                highlighted_edits.highlights[0].1.background_color,
15391                Some(cx.theme().status().deleted_background)
15392            );
15393        },
15394    )
15395    .await;
15396
15397    // Insertion
15398    assert_highlighted_edits(
15399        "Hello, world!",
15400        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15401        true,
15402        cx,
15403        |highlighted_edits, cx| {
15404            assert_eq!(highlighted_edits.highlights.len(), 1);
15405            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15406            assert_eq!(
15407                highlighted_edits.highlights[0].1.background_color,
15408                Some(cx.theme().status().created_background)
15409            );
15410        },
15411    )
15412    .await;
15413}
15414
15415async fn assert_highlighted_edits(
15416    text: &str,
15417    edits: Vec<(Range<Point>, String)>,
15418    include_deletions: bool,
15419    cx: &mut TestAppContext,
15420    assertion_fn: impl Fn(HighlightedText, &App),
15421) {
15422    let window = cx.add_window(|window, cx| {
15423        let buffer = MultiBuffer::build_simple(text, cx);
15424        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15425    });
15426    let cx = &mut VisualTestContext::from_window(*window, cx);
15427
15428    let (buffer, snapshot) = window
15429        .update(cx, |editor, _window, cx| {
15430            (
15431                editor.buffer().clone(),
15432                editor.buffer().read(cx).snapshot(cx),
15433            )
15434        })
15435        .unwrap();
15436
15437    let edits = edits
15438        .into_iter()
15439        .map(|(range, edit)| {
15440            (
15441                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15442                edit,
15443            )
15444        })
15445        .collect::<Vec<_>>();
15446
15447    let text_anchor_edits = edits
15448        .clone()
15449        .into_iter()
15450        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15451        .collect::<Vec<_>>();
15452
15453    let edit_preview = window
15454        .update(cx, |_, _window, cx| {
15455            buffer
15456                .read(cx)
15457                .as_singleton()
15458                .unwrap()
15459                .read(cx)
15460                .preview_edits(text_anchor_edits.into(), cx)
15461        })
15462        .unwrap()
15463        .await;
15464
15465    cx.update(|_window, cx| {
15466        let highlighted_edits = inline_completion_edit_text(
15467            &snapshot.as_singleton().unwrap().2,
15468            &edits,
15469            &edit_preview,
15470            include_deletions,
15471            cx,
15472        )
15473        .expect("Missing highlighted edits");
15474        assertion_fn(highlighted_edits, cx)
15475    });
15476}
15477
15478#[gpui::test]
15479async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15480    init_test(cx, |_| {});
15481    let capabilities = lsp::ServerCapabilities {
15482        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15483            prepare_provider: Some(true),
15484            work_done_progress_options: Default::default(),
15485        })),
15486        ..Default::default()
15487    };
15488    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15489
15490    cx.set_state(indoc! {"
15491        struct Fˇoo {}
15492    "});
15493
15494    cx.update_editor(|editor, _, cx| {
15495        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15496        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15497        editor.highlight_background::<DocumentHighlightRead>(
15498            &[highlight_range],
15499            |c| c.editor_document_highlight_read_background,
15500            cx,
15501        );
15502    });
15503
15504    let mut prepare_rename_handler =
15505        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15506            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15507                start: lsp::Position {
15508                    line: 0,
15509                    character: 7,
15510                },
15511                end: lsp::Position {
15512                    line: 0,
15513                    character: 10,
15514                },
15515            })))
15516        });
15517    let prepare_rename_task = cx
15518        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15519        .expect("Prepare rename was not started");
15520    prepare_rename_handler.next().await.unwrap();
15521    prepare_rename_task.await.expect("Prepare rename failed");
15522
15523    let mut rename_handler =
15524        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15525            let edit = lsp::TextEdit {
15526                range: lsp::Range {
15527                    start: lsp::Position {
15528                        line: 0,
15529                        character: 7,
15530                    },
15531                    end: lsp::Position {
15532                        line: 0,
15533                        character: 10,
15534                    },
15535                },
15536                new_text: "FooRenamed".to_string(),
15537            };
15538            Ok(Some(lsp::WorkspaceEdit::new(
15539                // Specify the same edit twice
15540                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15541            )))
15542        });
15543    let rename_task = cx
15544        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15545        .expect("Confirm rename was not started");
15546    rename_handler.next().await.unwrap();
15547    rename_task.await.expect("Confirm rename failed");
15548    cx.run_until_parked();
15549
15550    // Despite two edits, only one is actually applied as those are identical
15551    cx.assert_editor_state(indoc! {"
15552        struct FooRenamedˇ {}
15553    "});
15554}
15555
15556#[gpui::test]
15557async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15558    init_test(cx, |_| {});
15559    // These capabilities indicate that the server does not support prepare rename.
15560    let capabilities = lsp::ServerCapabilities {
15561        rename_provider: Some(lsp::OneOf::Left(true)),
15562        ..Default::default()
15563    };
15564    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15565
15566    cx.set_state(indoc! {"
15567        struct Fˇoo {}
15568    "});
15569
15570    cx.update_editor(|editor, _window, cx| {
15571        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15572        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15573        editor.highlight_background::<DocumentHighlightRead>(
15574            &[highlight_range],
15575            |c| c.editor_document_highlight_read_background,
15576            cx,
15577        );
15578    });
15579
15580    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15581        .expect("Prepare rename was not started")
15582        .await
15583        .expect("Prepare rename failed");
15584
15585    let mut rename_handler =
15586        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15587            let edit = lsp::TextEdit {
15588                range: lsp::Range {
15589                    start: lsp::Position {
15590                        line: 0,
15591                        character: 7,
15592                    },
15593                    end: lsp::Position {
15594                        line: 0,
15595                        character: 10,
15596                    },
15597                },
15598                new_text: "FooRenamed".to_string(),
15599            };
15600            Ok(Some(lsp::WorkspaceEdit::new(
15601                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15602            )))
15603        });
15604    let rename_task = cx
15605        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15606        .expect("Confirm rename was not started");
15607    rename_handler.next().await.unwrap();
15608    rename_task.await.expect("Confirm rename failed");
15609    cx.run_until_parked();
15610
15611    // Correct range is renamed, as `surrounding_word` is used to find it.
15612    cx.assert_editor_state(indoc! {"
15613        struct FooRenamedˇ {}
15614    "});
15615}
15616
15617fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15618    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15619    point..point
15620}
15621
15622fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15623    let (text, ranges) = marked_text_ranges(marked_text, true);
15624    assert_eq!(editor.text(cx), text);
15625    assert_eq!(
15626        editor.selections.ranges(cx),
15627        ranges,
15628        "Assert selections are {}",
15629        marked_text
15630    );
15631}
15632
15633pub fn handle_signature_help_request(
15634    cx: &mut EditorLspTestContext,
15635    mocked_response: lsp::SignatureHelp,
15636) -> impl Future<Output = ()> {
15637    let mut request =
15638        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15639            let mocked_response = mocked_response.clone();
15640            async move { Ok(Some(mocked_response)) }
15641        });
15642
15643    async move {
15644        request.next().await;
15645    }
15646}
15647
15648/// Handle completion request passing a marked string specifying where the completion
15649/// should be triggered from using '|' character, what range should be replaced, and what completions
15650/// should be returned using '<' and '>' to delimit the range
15651pub fn handle_completion_request(
15652    cx: &mut EditorLspTestContext,
15653    marked_string: &str,
15654    completions: Vec<&'static str>,
15655    counter: Arc<AtomicUsize>,
15656) -> impl Future<Output = ()> {
15657    let complete_from_marker: TextRangeMarker = '|'.into();
15658    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15659    let (_, mut marked_ranges) = marked_text_ranges_by(
15660        marked_string,
15661        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15662    );
15663
15664    let complete_from_position =
15665        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15666    let replace_range =
15667        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15668
15669    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15670        let completions = completions.clone();
15671        counter.fetch_add(1, atomic::Ordering::Release);
15672        async move {
15673            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15674            assert_eq!(
15675                params.text_document_position.position,
15676                complete_from_position
15677            );
15678            Ok(Some(lsp::CompletionResponse::Array(
15679                completions
15680                    .iter()
15681                    .map(|completion_text| lsp::CompletionItem {
15682                        label: completion_text.to_string(),
15683                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15684                            range: replace_range,
15685                            new_text: completion_text.to_string(),
15686                        })),
15687                        ..Default::default()
15688                    })
15689                    .collect(),
15690            )))
15691        }
15692    });
15693
15694    async move {
15695        request.next().await;
15696    }
15697}
15698
15699fn handle_resolve_completion_request(
15700    cx: &mut EditorLspTestContext,
15701    edits: Option<Vec<(&'static str, &'static str)>>,
15702) -> impl Future<Output = ()> {
15703    let edits = edits.map(|edits| {
15704        edits
15705            .iter()
15706            .map(|(marked_string, new_text)| {
15707                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15708                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15709                lsp::TextEdit::new(replace_range, new_text.to_string())
15710            })
15711            .collect::<Vec<_>>()
15712    });
15713
15714    let mut request =
15715        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15716            let edits = edits.clone();
15717            async move {
15718                Ok(lsp::CompletionItem {
15719                    additional_text_edits: edits,
15720                    ..Default::default()
15721                })
15722            }
15723        });
15724
15725    async move {
15726        request.next().await;
15727    }
15728}
15729
15730pub(crate) fn update_test_language_settings(
15731    cx: &mut TestAppContext,
15732    f: impl Fn(&mut AllLanguageSettingsContent),
15733) {
15734    cx.update(|cx| {
15735        SettingsStore::update_global(cx, |store, cx| {
15736            store.update_user_settings::<AllLanguageSettings>(cx, f);
15737        });
15738    });
15739}
15740
15741pub(crate) fn update_test_project_settings(
15742    cx: &mut TestAppContext,
15743    f: impl Fn(&mut ProjectSettings),
15744) {
15745    cx.update(|cx| {
15746        SettingsStore::update_global(cx, |store, cx| {
15747            store.update_user_settings::<ProjectSettings>(cx, f);
15748        });
15749    });
15750}
15751
15752pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15753    cx.update(|cx| {
15754        assets::Assets.load_test_fonts(cx);
15755        let store = SettingsStore::test(cx);
15756        cx.set_global(store);
15757        theme::init(theme::LoadThemes::JustBase, cx);
15758        release_channel::init(SemanticVersion::default(), cx);
15759        client::init_settings(cx);
15760        language::init(cx);
15761        Project::init_settings(cx);
15762        workspace::init_settings(cx);
15763        crate::init(cx);
15764    });
15765
15766    update_test_language_settings(cx, f);
15767}
15768
15769#[track_caller]
15770fn assert_hunk_revert(
15771    not_reverted_text_with_selections: &str,
15772    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15773    expected_reverted_text_with_selections: &str,
15774    base_text: &str,
15775    cx: &mut EditorLspTestContext,
15776) {
15777    cx.set_state(not_reverted_text_with_selections);
15778    cx.set_diff_base(base_text);
15779    cx.executor().run_until_parked();
15780
15781    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15782        let snapshot = editor.snapshot(window, cx);
15783        let reverted_hunk_statuses = snapshot
15784            .buffer_snapshot
15785            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15786            .map(|hunk| hunk.status())
15787            .collect::<Vec<_>>();
15788
15789        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15790        reverted_hunk_statuses
15791    });
15792    cx.executor().run_until_parked();
15793    cx.assert_editor_state(expected_reverted_text_with_selections);
15794    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15795}