editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    code_context_menus::CodeContextMenu,
    5    inline_completion_tests::FakeInlineCompletionProvider,
    6    linked_editing_ranges::LinkedEditingRanges,
    7    scroll::scroll_amount::ScrollAmount,
    8    test::{
    9        assert_text_with_selections, build_editor,
   10        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
   11        editor_test_context::EditorTestContext,
   12        select_ranges,
   13    },
   14};
   15use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   16use futures::StreamExt;
   17use gpui::{
   18    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   19    VisualTestContext, WindowBounds, WindowOptions, div,
   20};
   21use indoc::indoc;
   22use language::{
   23    BracketPairConfig,
   24    Capability::ReadWrite,
   25    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   26    Override, Point,
   27    language_settings::{
   28        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   29        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   30    },
   31    tree_sitter_python,
   32};
   33use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   34use lsp::CompletionParams;
   35use multi_buffer::{IndentGuide, PathKey};
   36use parking_lot::Mutex;
   37use pretty_assertions::{assert_eq, assert_ne};
   38use project::{
   39    FakeFs,
   40    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   41    project_settings::{LspSettings, ProjectSettings},
   42};
   43use serde_json::{self, json};
   44use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   45use std::{
   46    iter,
   47    sync::atomic::{self, AtomicUsize},
   48};
   49use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   50use text::ToPoint as _;
   51use unindent::Unindent;
   52use util::{
   53    assert_set_eq, path,
   54    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   55    uri,
   56};
   57use workspace::{
   58    CloseActiveItem, CloseAllItems, CloseInactiveItems, NavigationEntry, OpenOptions, ViewId,
   59    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   60};
   61
   62#[gpui::test]
   63fn test_edit_events(cx: &mut TestAppContext) {
   64    init_test(cx, |_| {});
   65
   66    let buffer = cx.new(|cx| {
   67        let mut buffer = language::Buffer::local("123456", cx);
   68        buffer.set_group_interval(Duration::from_secs(1));
   69        buffer
   70    });
   71
   72    let events = Rc::new(RefCell::new(Vec::new()));
   73    let editor1 = cx.add_window({
   74        let events = events.clone();
   75        |window, cx| {
   76            let entity = cx.entity().clone();
   77            cx.subscribe_in(
   78                &entity,
   79                window,
   80                move |_, _, event: &EditorEvent, _, _| match event {
   81                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   82                    EditorEvent::BufferEdited => {
   83                        events.borrow_mut().push(("editor1", "buffer edited"))
   84                    }
   85                    _ => {}
   86                },
   87            )
   88            .detach();
   89            Editor::for_buffer(buffer.clone(), None, window, cx)
   90        }
   91    });
   92
   93    let editor2 = cx.add_window({
   94        let events = events.clone();
   95        |window, cx| {
   96            cx.subscribe_in(
   97                &cx.entity().clone(),
   98                window,
   99                move |_, _, event: &EditorEvent, _, _| match event {
  100                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
  101                    EditorEvent::BufferEdited => {
  102                        events.borrow_mut().push(("editor2", "buffer edited"))
  103                    }
  104                    _ => {}
  105                },
  106            )
  107            .detach();
  108            Editor::for_buffer(buffer.clone(), None, window, cx)
  109        }
  110    });
  111
  112    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  113
  114    // Mutating editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Mutating editor 2 will emit an `Edited` event only for that editor.
  126    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor2", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  137    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, 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    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  148    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor1", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  159    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, 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    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  170    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  171    assert_eq!(
  172        mem::take(&mut *events.borrow_mut()),
  173        [
  174            ("editor2", "edited"),
  175            ("editor1", "buffer edited"),
  176            ("editor2", "buffer edited"),
  177        ]
  178    );
  179
  180    // No event is emitted when the mutation is a no-op.
  181    _ = editor2.update(cx, |editor, window, cx| {
  182        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  183
  184        editor.backspace(&Backspace, window, cx);
  185    });
  186    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  187}
  188
  189#[gpui::test]
  190fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  191    init_test(cx, |_| {});
  192
  193    let mut now = Instant::now();
  194    let group_interval = Duration::from_millis(1);
  195    let buffer = cx.new(|cx| {
  196        let mut buf = language::Buffer::local("123456", cx);
  197        buf.set_group_interval(group_interval);
  198        buf
  199    });
  200    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  201    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  202
  203    _ = editor.update(cx, |editor, window, cx| {
  204        editor.start_transaction_at(now, window, cx);
  205        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  206
  207        editor.insert("cd", window, cx);
  208        editor.end_transaction_at(now, cx);
  209        assert_eq!(editor.text(cx), "12cd56");
  210        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  211
  212        editor.start_transaction_at(now, window, cx);
  213        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  214        editor.insert("e", window, cx);
  215        editor.end_transaction_at(now, cx);
  216        assert_eq!(editor.text(cx), "12cde6");
  217        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  218
  219        now += group_interval + Duration::from_millis(1);
  220        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  221
  222        // Simulate an edit in another editor
  223        buffer.update(cx, |buffer, cx| {
  224            buffer.start_transaction_at(now, cx);
  225            buffer.edit([(0..1, "a")], None, cx);
  226            buffer.edit([(1..1, "b")], None, cx);
  227            buffer.end_transaction_at(now, cx);
  228        });
  229
  230        assert_eq!(editor.text(cx), "ab2cde6");
  231        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  232
  233        // Last transaction happened past the group interval in a different editor.
  234        // Undo it individually and don't restore selections.
  235        editor.undo(&Undo, window, cx);
  236        assert_eq!(editor.text(cx), "12cde6");
  237        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  238
  239        // First two transactions happened within the group interval in this editor.
  240        // Undo them together and restore selections.
  241        editor.undo(&Undo, window, cx);
  242        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  243        assert_eq!(editor.text(cx), "123456");
  244        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  245
  246        // Redo the first two transactions together.
  247        editor.redo(&Redo, window, cx);
  248        assert_eq!(editor.text(cx), "12cde6");
  249        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  250
  251        // Redo the last transaction on its own.
  252        editor.redo(&Redo, window, cx);
  253        assert_eq!(editor.text(cx), "ab2cde6");
  254        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  255
  256        // Test empty transactions.
  257        editor.start_transaction_at(now, window, cx);
  258        editor.end_transaction_at(now, cx);
  259        editor.undo(&Undo, window, cx);
  260        assert_eq!(editor.text(cx), "12cde6");
  261    });
  262}
  263
  264#[gpui::test]
  265fn test_ime_composition(cx: &mut TestAppContext) {
  266    init_test(cx, |_| {});
  267
  268    let buffer = cx.new(|cx| {
  269        let mut buffer = language::Buffer::local("abcde", cx);
  270        // Ensure automatic grouping doesn't occur.
  271        buffer.set_group_interval(Duration::ZERO);
  272        buffer
  273    });
  274
  275    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  276    cx.add_window(|window, cx| {
  277        let mut editor = build_editor(buffer.clone(), window, cx);
  278
  279        // Start a new IME composition.
  280        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  281        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  282        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  283        assert_eq!(editor.text(cx), "äbcde");
  284        assert_eq!(
  285            editor.marked_text_ranges(cx),
  286            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  287        );
  288
  289        // Finalize IME composition.
  290        editor.replace_text_in_range(None, "ā", window, cx);
  291        assert_eq!(editor.text(cx), "ābcde");
  292        assert_eq!(editor.marked_text_ranges(cx), None);
  293
  294        // IME composition edits are grouped and are undone/redone at once.
  295        editor.undo(&Default::default(), window, cx);
  296        assert_eq!(editor.text(cx), "abcde");
  297        assert_eq!(editor.marked_text_ranges(cx), None);
  298        editor.redo(&Default::default(), window, cx);
  299        assert_eq!(editor.text(cx), "ābcde");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition.
  303        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  304        assert_eq!(
  305            editor.marked_text_ranges(cx),
  306            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  307        );
  308
  309        // Undoing during an IME composition cancels it.
  310        editor.undo(&Default::default(), window, cx);
  311        assert_eq!(editor.text(cx), "ābcde");
  312        assert_eq!(editor.marked_text_ranges(cx), None);
  313
  314        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  315        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  316        assert_eq!(editor.text(cx), "ābcdè");
  317        assert_eq!(
  318            editor.marked_text_ranges(cx),
  319            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  320        );
  321
  322        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  323        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  324        assert_eq!(editor.text(cx), "ābcdę");
  325        assert_eq!(editor.marked_text_ranges(cx), None);
  326
  327        // Start a new IME composition with multiple cursors.
  328        editor.change_selections(None, window, cx, |s| {
  329            s.select_ranges([
  330                OffsetUtf16(1)..OffsetUtf16(1),
  331                OffsetUtf16(3)..OffsetUtf16(3),
  332                OffsetUtf16(5)..OffsetUtf16(5),
  333            ])
  334        });
  335        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  336        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  337        assert_eq!(
  338            editor.marked_text_ranges(cx),
  339            Some(vec![
  340                OffsetUtf16(0)..OffsetUtf16(3),
  341                OffsetUtf16(4)..OffsetUtf16(7),
  342                OffsetUtf16(8)..OffsetUtf16(11)
  343            ])
  344        );
  345
  346        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  347        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  348        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  349        assert_eq!(
  350            editor.marked_text_ranges(cx),
  351            Some(vec![
  352                OffsetUtf16(1)..OffsetUtf16(2),
  353                OffsetUtf16(5)..OffsetUtf16(6),
  354                OffsetUtf16(9)..OffsetUtf16(10)
  355            ])
  356        );
  357
  358        // Finalize IME composition with multiple cursors.
  359        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  360        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  361        assert_eq!(editor.marked_text_ranges(cx), None);
  362
  363        editor
  364    });
  365}
  366
  367#[gpui::test]
  368fn test_selection_with_mouse(cx: &mut TestAppContext) {
  369    init_test(cx, |_| {});
  370
  371    let editor = cx.add_window(|window, cx| {
  372        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  373        build_editor(buffer, window, cx)
  374    });
  375
  376    _ = editor.update(cx, |editor, window, cx| {
  377        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  378    });
  379    assert_eq!(
  380        editor
  381            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  382            .unwrap(),
  383        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  384    );
  385
  386    _ = editor.update(cx, |editor, window, cx| {
  387        editor.update_selection(
  388            DisplayPoint::new(DisplayRow(3), 3),
  389            0,
  390            gpui::Point::<f32>::default(),
  391            window,
  392            cx,
  393        );
  394    });
  395
  396    assert_eq!(
  397        editor
  398            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  399            .unwrap(),
  400        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  401    );
  402
  403    _ = editor.update(cx, |editor, window, cx| {
  404        editor.update_selection(
  405            DisplayPoint::new(DisplayRow(1), 1),
  406            0,
  407            gpui::Point::<f32>::default(),
  408            window,
  409            cx,
  410        );
  411    });
  412
  413    assert_eq!(
  414        editor
  415            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  416            .unwrap(),
  417        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  418    );
  419
  420    _ = editor.update(cx, |editor, window, cx| {
  421        editor.end_selection(window, cx);
  422        editor.update_selection(
  423            DisplayPoint::new(DisplayRow(3), 3),
  424            0,
  425            gpui::Point::<f32>::default(),
  426            window,
  427            cx,
  428        );
  429    });
  430
  431    assert_eq!(
  432        editor
  433            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  434            .unwrap(),
  435        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  436    );
  437
  438    _ = editor.update(cx, |editor, window, cx| {
  439        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  440        editor.update_selection(
  441            DisplayPoint::new(DisplayRow(0), 0),
  442            0,
  443            gpui::Point::<f32>::default(),
  444            window,
  445            cx,
  446        );
  447    });
  448
  449    assert_eq!(
  450        editor
  451            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  452            .unwrap(),
  453        [
  454            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  455            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  456        ]
  457    );
  458
  459    _ = editor.update(cx, |editor, window, cx| {
  460        editor.end_selection(window, cx);
  461    });
  462
  463    assert_eq!(
  464        editor
  465            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  466            .unwrap(),
  467        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  468    );
  469}
  470
  471#[gpui::test]
  472fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  473    init_test(cx, |_| {});
  474
  475    let editor = cx.add_window(|window, cx| {
  476        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  477        build_editor(buffer, window, cx)
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.end_selection(window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  490    });
  491
  492    _ = editor.update(cx, |editor, window, cx| {
  493        editor.end_selection(window, cx);
  494    });
  495
  496    assert_eq!(
  497        editor
  498            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  499            .unwrap(),
  500        [
  501            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  502            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  503        ]
  504    );
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  508    });
  509
  510    _ = editor.update(cx, |editor, window, cx| {
  511        editor.end_selection(window, cx);
  512    });
  513
  514    assert_eq!(
  515        editor
  516            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  517            .unwrap(),
  518        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  519    );
  520}
  521
  522#[gpui::test]
  523fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  524    init_test(cx, |_| {});
  525
  526    let editor = cx.add_window(|window, cx| {
  527        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  528        build_editor(buffer, window, cx)
  529    });
  530
  531    _ = editor.update(cx, |editor, window, cx| {
  532        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  533        assert_eq!(
  534            editor.selections.display_ranges(cx),
  535            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  536        );
  537    });
  538
  539    _ = editor.update(cx, |editor, window, cx| {
  540        editor.update_selection(
  541            DisplayPoint::new(DisplayRow(3), 3),
  542            0,
  543            gpui::Point::<f32>::default(),
  544            window,
  545            cx,
  546        );
  547        assert_eq!(
  548            editor.selections.display_ranges(cx),
  549            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  550        );
  551    });
  552
  553    _ = editor.update(cx, |editor, window, cx| {
  554        editor.cancel(&Cancel, window, cx);
  555        editor.update_selection(
  556            DisplayPoint::new(DisplayRow(1), 1),
  557            0,
  558            gpui::Point::<f32>::default(),
  559            window,
  560            cx,
  561        );
  562        assert_eq!(
  563            editor.selections.display_ranges(cx),
  564            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  565        );
  566    });
  567}
  568
  569#[gpui::test]
  570fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  571    init_test(cx, |_| {});
  572
  573    let editor = cx.add_window(|window, cx| {
  574        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  575        build_editor(buffer, window, cx)
  576    });
  577
  578    _ = editor.update(cx, |editor, window, cx| {
  579        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  580        assert_eq!(
  581            editor.selections.display_ranges(cx),
  582            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  583        );
  584
  585        editor.move_down(&Default::default(), window, cx);
  586        assert_eq!(
  587            editor.selections.display_ranges(cx),
  588            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  589        );
  590
  591        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  592        assert_eq!(
  593            editor.selections.display_ranges(cx),
  594            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  595        );
  596
  597        editor.move_up(&Default::default(), window, cx);
  598        assert_eq!(
  599            editor.selections.display_ranges(cx),
  600            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  601        );
  602    });
  603}
  604
  605#[gpui::test]
  606fn test_clone(cx: &mut TestAppContext) {
  607    init_test(cx, |_| {});
  608
  609    let (text, selection_ranges) = marked_text_ranges(
  610        indoc! {"
  611            one
  612            two
  613            threeˇ
  614            four
  615            fiveˇ
  616        "},
  617        true,
  618    );
  619
  620    let editor = cx.add_window(|window, cx| {
  621        let buffer = MultiBuffer::build_simple(&text, cx);
  622        build_editor(buffer, window, cx)
  623    });
  624
  625    _ = editor.update(cx, |editor, window, cx| {
  626        editor.change_selections(None, window, cx, |s| {
  627            s.select_ranges(selection_ranges.clone())
  628        });
  629        editor.fold_creases(
  630            vec![
  631                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  632                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  633            ],
  634            true,
  635            window,
  636            cx,
  637        );
  638    });
  639
  640    let cloned_editor = editor
  641        .update(cx, |editor, _, cx| {
  642            cx.open_window(Default::default(), |window, cx| {
  643                cx.new(|cx| editor.clone(window, cx))
  644            })
  645        })
  646        .unwrap()
  647        .unwrap();
  648
  649    let snapshot = editor
  650        .update(cx, |e, window, cx| e.snapshot(window, cx))
  651        .unwrap();
  652    let cloned_snapshot = cloned_editor
  653        .update(cx, |e, window, cx| e.snapshot(window, cx))
  654        .unwrap();
  655
  656    assert_eq!(
  657        cloned_editor
  658            .update(cx, |e, _, cx| e.display_text(cx))
  659            .unwrap(),
  660        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  661    );
  662    assert_eq!(
  663        cloned_snapshot
  664            .folds_in_range(0..text.len())
  665            .collect::<Vec<_>>(),
  666        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  667    );
  668    assert_set_eq!(
  669        cloned_editor
  670            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  671            .unwrap(),
  672        editor
  673            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  674            .unwrap()
  675    );
  676    assert_set_eq!(
  677        cloned_editor
  678            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  679            .unwrap(),
  680        editor
  681            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  682            .unwrap()
  683    );
  684}
  685
  686#[gpui::test]
  687async fn test_navigation_history(cx: &mut TestAppContext) {
  688    init_test(cx, |_| {});
  689
  690    use workspace::item::Item;
  691
  692    let fs = FakeFs::new(cx.executor());
  693    let project = Project::test(fs, [], cx).await;
  694    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  695    let pane = workspace
  696        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  697        .unwrap();
  698
  699    _ = workspace.update(cx, |_v, window, cx| {
  700        cx.new(|cx| {
  701            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  702            let mut editor = build_editor(buffer.clone(), window, cx);
  703            let handle = cx.entity();
  704            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  705
  706            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  707                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  708            }
  709
  710            // Move the cursor a small distance.
  711            // Nothing is added to the navigation history.
  712            editor.change_selections(None, window, cx, |s| {
  713                s.select_display_ranges([
  714                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  715                ])
  716            });
  717            editor.change_selections(None, window, cx, |s| {
  718                s.select_display_ranges([
  719                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  720                ])
  721            });
  722            assert!(pop_history(&mut editor, cx).is_none());
  723
  724            // Move the cursor a large distance.
  725            // The history can jump back to the previous position.
  726            editor.change_selections(None, window, cx, |s| {
  727                s.select_display_ranges([
  728                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  729                ])
  730            });
  731            let nav_entry = pop_history(&mut editor, cx).unwrap();
  732            editor.navigate(nav_entry.data.unwrap(), window, cx);
  733            assert_eq!(nav_entry.item.id(), cx.entity_id());
  734            assert_eq!(
  735                editor.selections.display_ranges(cx),
  736                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  737            );
  738            assert!(pop_history(&mut editor, cx).is_none());
  739
  740            // Move the cursor a small distance via the mouse.
  741            // Nothing is added to the navigation history.
  742            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  743            editor.end_selection(window, cx);
  744            assert_eq!(
  745                editor.selections.display_ranges(cx),
  746                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  747            );
  748            assert!(pop_history(&mut editor, cx).is_none());
  749
  750            // Move the cursor a large distance via the mouse.
  751            // The history can jump back to the previous position.
  752            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  753            editor.end_selection(window, cx);
  754            assert_eq!(
  755                editor.selections.display_ranges(cx),
  756                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  757            );
  758            let nav_entry = pop_history(&mut editor, cx).unwrap();
  759            editor.navigate(nav_entry.data.unwrap(), window, cx);
  760            assert_eq!(nav_entry.item.id(), cx.entity_id());
  761            assert_eq!(
  762                editor.selections.display_ranges(cx),
  763                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  764            );
  765            assert!(pop_history(&mut editor, cx).is_none());
  766
  767            // Set scroll position to check later
  768            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  769            let original_scroll_position = editor.scroll_manager.anchor();
  770
  771            // Jump to the end of the document and adjust scroll
  772            editor.move_to_end(&MoveToEnd, window, cx);
  773            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  774            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            let nav_entry = pop_history(&mut editor, cx).unwrap();
  777            editor.navigate(nav_entry.data.unwrap(), window, cx);
  778            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  779
  780            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  781            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  782            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  783            let invalid_point = Point::new(9999, 0);
  784            editor.navigate(
  785                Box::new(NavigationData {
  786                    cursor_anchor: invalid_anchor,
  787                    cursor_position: invalid_point,
  788                    scroll_anchor: ScrollAnchor {
  789                        anchor: invalid_anchor,
  790                        offset: Default::default(),
  791                    },
  792                    scroll_top_row: invalid_point.row,
  793                }),
  794                window,
  795                cx,
  796            );
  797            assert_eq!(
  798                editor.selections.display_ranges(cx),
  799                &[editor.max_point(cx)..editor.max_point(cx)]
  800            );
  801            assert_eq!(
  802                editor.scroll_position(cx),
  803                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  804            );
  805
  806            editor
  807        })
  808    });
  809}
  810
  811#[gpui::test]
  812fn test_cancel(cx: &mut TestAppContext) {
  813    init_test(cx, |_| {});
  814
  815    let editor = cx.add_window(|window, cx| {
  816        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  817        build_editor(buffer, window, cx)
  818    });
  819
  820    _ = editor.update(cx, |editor, window, cx| {
  821        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  822        editor.update_selection(
  823            DisplayPoint::new(DisplayRow(1), 1),
  824            0,
  825            gpui::Point::<f32>::default(),
  826            window,
  827            cx,
  828        );
  829        editor.end_selection(window, cx);
  830
  831        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  832        editor.update_selection(
  833            DisplayPoint::new(DisplayRow(0), 3),
  834            0,
  835            gpui::Point::<f32>::default(),
  836            window,
  837            cx,
  838        );
  839        editor.end_selection(window, cx);
  840        assert_eq!(
  841            editor.selections.display_ranges(cx),
  842            [
  843                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  844                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  845            ]
  846        );
  847    });
  848
  849    _ = editor.update(cx, |editor, window, cx| {
  850        editor.cancel(&Cancel, window, cx);
  851        assert_eq!(
  852            editor.selections.display_ranges(cx),
  853            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  854        );
  855    });
  856
  857    _ = editor.update(cx, |editor, window, cx| {
  858        editor.cancel(&Cancel, window, cx);
  859        assert_eq!(
  860            editor.selections.display_ranges(cx),
  861            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  862        );
  863    });
  864}
  865
  866#[gpui::test]
  867fn test_fold_action(cx: &mut TestAppContext) {
  868    init_test(cx, |_| {});
  869
  870    let editor = cx.add_window(|window, cx| {
  871        let buffer = MultiBuffer::build_simple(
  872            &"
  873                impl Foo {
  874                    // Hello!
  875
  876                    fn a() {
  877                        1
  878                    }
  879
  880                    fn b() {
  881                        2
  882                    }
  883
  884                    fn c() {
  885                        3
  886                    }
  887                }
  888            "
  889            .unindent(),
  890            cx,
  891        );
  892        build_editor(buffer.clone(), window, cx)
  893    });
  894
  895    _ = editor.update(cx, |editor, window, cx| {
  896        editor.change_selections(None, window, cx, |s| {
  897            s.select_display_ranges([
  898                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  899            ]);
  900        });
  901        editor.fold(&Fold, window, cx);
  902        assert_eq!(
  903            editor.display_text(cx),
  904            "
  905                impl Foo {
  906                    // Hello!
  907
  908                    fn a() {
  909                        1
  910                    }
  911
  912                    fn b() {⋯
  913                    }
  914
  915                    fn c() {⋯
  916                    }
  917                }
  918            "
  919            .unindent(),
  920        );
  921
  922        editor.fold(&Fold, window, cx);
  923        assert_eq!(
  924            editor.display_text(cx),
  925            "
  926                impl Foo {⋯
  927                }
  928            "
  929            .unindent(),
  930        );
  931
  932        editor.unfold_lines(&UnfoldLines, window, cx);
  933        assert_eq!(
  934            editor.display_text(cx),
  935            "
  936                impl Foo {
  937                    // Hello!
  938
  939                    fn a() {
  940                        1
  941                    }
  942
  943                    fn b() {⋯
  944                    }
  945
  946                    fn c() {⋯
  947                    }
  948                }
  949            "
  950            .unindent(),
  951        );
  952
  953        editor.unfold_lines(&UnfoldLines, window, cx);
  954        assert_eq!(
  955            editor.display_text(cx),
  956            editor.buffer.read(cx).read(cx).text()
  957        );
  958    });
  959}
  960
  961#[gpui::test]
  962fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  963    init_test(cx, |_| {});
  964
  965    let editor = cx.add_window(|window, cx| {
  966        let buffer = MultiBuffer::build_simple(
  967            &"
  968                class Foo:
  969                    # Hello!
  970
  971                    def a():
  972                        print(1)
  973
  974                    def b():
  975                        print(2)
  976
  977                    def c():
  978                        print(3)
  979            "
  980            .unindent(),
  981            cx,
  982        );
  983        build_editor(buffer.clone(), window, cx)
  984    });
  985
  986    _ = editor.update(cx, |editor, window, cx| {
  987        editor.change_selections(None, window, cx, |s| {
  988            s.select_display_ranges([
  989                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  990            ]);
  991        });
  992        editor.fold(&Fold, window, cx);
  993        assert_eq!(
  994            editor.display_text(cx),
  995            "
  996                class Foo:
  997                    # Hello!
  998
  999                    def a():
 1000                        print(1)
 1001
 1002                    def b():⋯
 1003
 1004                    def c():⋯
 1005            "
 1006            .unindent(),
 1007        );
 1008
 1009        editor.fold(&Fold, window, cx);
 1010        assert_eq!(
 1011            editor.display_text(cx),
 1012            "
 1013                class Foo:⋯
 1014            "
 1015            .unindent(),
 1016        );
 1017
 1018        editor.unfold_lines(&UnfoldLines, window, cx);
 1019        assert_eq!(
 1020            editor.display_text(cx),
 1021            "
 1022                class Foo:
 1023                    # Hello!
 1024
 1025                    def a():
 1026                        print(1)
 1027
 1028                    def b():⋯
 1029
 1030                    def c():⋯
 1031            "
 1032            .unindent(),
 1033        );
 1034
 1035        editor.unfold_lines(&UnfoldLines, window, cx);
 1036        assert_eq!(
 1037            editor.display_text(cx),
 1038            editor.buffer.read(cx).read(cx).text()
 1039        );
 1040    });
 1041}
 1042
 1043#[gpui::test]
 1044fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1045    init_test(cx, |_| {});
 1046
 1047    let editor = cx.add_window(|window, cx| {
 1048        let buffer = MultiBuffer::build_simple(
 1049            &"
 1050                class Foo:
 1051                    # Hello!
 1052
 1053                    def a():
 1054                        print(1)
 1055
 1056                    def b():
 1057                        print(2)
 1058
 1059
 1060                    def c():
 1061                        print(3)
 1062
 1063
 1064            "
 1065            .unindent(),
 1066            cx,
 1067        );
 1068        build_editor(buffer.clone(), window, cx)
 1069    });
 1070
 1071    _ = editor.update(cx, |editor, window, cx| {
 1072        editor.change_selections(None, window, cx, |s| {
 1073            s.select_display_ranges([
 1074                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1075            ]);
 1076        });
 1077        editor.fold(&Fold, window, cx);
 1078        assert_eq!(
 1079            editor.display_text(cx),
 1080            "
 1081                class Foo:
 1082                    # Hello!
 1083
 1084                    def a():
 1085                        print(1)
 1086
 1087                    def b():⋯
 1088
 1089
 1090                    def c():⋯
 1091
 1092
 1093            "
 1094            .unindent(),
 1095        );
 1096
 1097        editor.fold(&Fold, window, cx);
 1098        assert_eq!(
 1099            editor.display_text(cx),
 1100            "
 1101                class Foo:⋯
 1102
 1103
 1104            "
 1105            .unindent(),
 1106        );
 1107
 1108        editor.unfold_lines(&UnfoldLines, window, cx);
 1109        assert_eq!(
 1110            editor.display_text(cx),
 1111            "
 1112                class Foo:
 1113                    # Hello!
 1114
 1115                    def a():
 1116                        print(1)
 1117
 1118                    def b():⋯
 1119
 1120
 1121                    def c():⋯
 1122
 1123
 1124            "
 1125            .unindent(),
 1126        );
 1127
 1128        editor.unfold_lines(&UnfoldLines, window, cx);
 1129        assert_eq!(
 1130            editor.display_text(cx),
 1131            editor.buffer.read(cx).read(cx).text()
 1132        );
 1133    });
 1134}
 1135
 1136#[gpui::test]
 1137fn test_fold_at_level(cx: &mut TestAppContext) {
 1138    init_test(cx, |_| {});
 1139
 1140    let editor = cx.add_window(|window, cx| {
 1141        let buffer = MultiBuffer::build_simple(
 1142            &"
 1143                class Foo:
 1144                    # Hello!
 1145
 1146                    def a():
 1147                        print(1)
 1148
 1149                    def b():
 1150                        print(2)
 1151
 1152
 1153                class Bar:
 1154                    # World!
 1155
 1156                    def a():
 1157                        print(1)
 1158
 1159                    def b():
 1160                        print(2)
 1161
 1162
 1163            "
 1164            .unindent(),
 1165            cx,
 1166        );
 1167        build_editor(buffer.clone(), window, cx)
 1168    });
 1169
 1170    _ = editor.update(cx, |editor, window, cx| {
 1171        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1172        assert_eq!(
 1173            editor.display_text(cx),
 1174            "
 1175                class Foo:
 1176                    # Hello!
 1177
 1178                    def a():⋯
 1179
 1180                    def b():⋯
 1181
 1182
 1183                class Bar:
 1184                    # World!
 1185
 1186                    def a():⋯
 1187
 1188                    def b():⋯
 1189
 1190
 1191            "
 1192            .unindent(),
 1193        );
 1194
 1195        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1196        assert_eq!(
 1197            editor.display_text(cx),
 1198            "
 1199                class Foo:⋯
 1200
 1201
 1202                class Bar:⋯
 1203
 1204
 1205            "
 1206            .unindent(),
 1207        );
 1208
 1209        editor.unfold_all(&UnfoldAll, window, cx);
 1210        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1211        assert_eq!(
 1212            editor.display_text(cx),
 1213            "
 1214                class Foo:
 1215                    # Hello!
 1216
 1217                    def a():
 1218                        print(1)
 1219
 1220                    def b():
 1221                        print(2)
 1222
 1223
 1224                class Bar:
 1225                    # World!
 1226
 1227                    def a():
 1228                        print(1)
 1229
 1230                    def b():
 1231                        print(2)
 1232
 1233
 1234            "
 1235            .unindent(),
 1236        );
 1237
 1238        assert_eq!(
 1239            editor.display_text(cx),
 1240            editor.buffer.read(cx).read(cx).text()
 1241        );
 1242    });
 1243}
 1244
 1245#[gpui::test]
 1246fn test_move_cursor(cx: &mut TestAppContext) {
 1247    init_test(cx, |_| {});
 1248
 1249    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1250    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1251
 1252    buffer.update(cx, |buffer, cx| {
 1253        buffer.edit(
 1254            vec![
 1255                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1256                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1257            ],
 1258            None,
 1259            cx,
 1260        );
 1261    });
 1262    _ = editor.update(cx, |editor, window, cx| {
 1263        assert_eq!(
 1264            editor.selections.display_ranges(cx),
 1265            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1266        );
 1267
 1268        editor.move_down(&MoveDown, window, cx);
 1269        assert_eq!(
 1270            editor.selections.display_ranges(cx),
 1271            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1272        );
 1273
 1274        editor.move_right(&MoveRight, window, cx);
 1275        assert_eq!(
 1276            editor.selections.display_ranges(cx),
 1277            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1278        );
 1279
 1280        editor.move_left(&MoveLeft, window, cx);
 1281        assert_eq!(
 1282            editor.selections.display_ranges(cx),
 1283            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1284        );
 1285
 1286        editor.move_up(&MoveUp, window, cx);
 1287        assert_eq!(
 1288            editor.selections.display_ranges(cx),
 1289            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1290        );
 1291
 1292        editor.move_to_end(&MoveToEnd, window, cx);
 1293        assert_eq!(
 1294            editor.selections.display_ranges(cx),
 1295            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1296        );
 1297
 1298        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1299        assert_eq!(
 1300            editor.selections.display_ranges(cx),
 1301            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1302        );
 1303
 1304        editor.change_selections(None, window, cx, |s| {
 1305            s.select_display_ranges([
 1306                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1307            ]);
 1308        });
 1309        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1310        assert_eq!(
 1311            editor.selections.display_ranges(cx),
 1312            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1313        );
 1314
 1315        editor.select_to_end(&SelectToEnd, window, cx);
 1316        assert_eq!(
 1317            editor.selections.display_ranges(cx),
 1318            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1319        );
 1320    });
 1321}
 1322
 1323#[gpui::test]
 1324fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1325    init_test(cx, |_| {});
 1326
 1327    let editor = cx.add_window(|window, cx| {
 1328        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1329        build_editor(buffer.clone(), window, cx)
 1330    });
 1331
 1332    assert_eq!('🟥'.len_utf8(), 4);
 1333    assert_eq!('α'.len_utf8(), 2);
 1334
 1335    _ = editor.update(cx, |editor, window, cx| {
 1336        editor.fold_creases(
 1337            vec![
 1338                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1339                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1340                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1341            ],
 1342            true,
 1343            window,
 1344            cx,
 1345        );
 1346        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1347
 1348        editor.move_right(&MoveRight, window, cx);
 1349        assert_eq!(
 1350            editor.selections.display_ranges(cx),
 1351            &[empty_range(0, "🟥".len())]
 1352        );
 1353        editor.move_right(&MoveRight, window, cx);
 1354        assert_eq!(
 1355            editor.selections.display_ranges(cx),
 1356            &[empty_range(0, "🟥🟧".len())]
 1357        );
 1358        editor.move_right(&MoveRight, window, cx);
 1359        assert_eq!(
 1360            editor.selections.display_ranges(cx),
 1361            &[empty_range(0, "🟥🟧⋯".len())]
 1362        );
 1363
 1364        editor.move_down(&MoveDown, window, cx);
 1365        assert_eq!(
 1366            editor.selections.display_ranges(cx),
 1367            &[empty_range(1, "ab⋯e".len())]
 1368        );
 1369        editor.move_left(&MoveLeft, window, cx);
 1370        assert_eq!(
 1371            editor.selections.display_ranges(cx),
 1372            &[empty_range(1, "ab⋯".len())]
 1373        );
 1374        editor.move_left(&MoveLeft, window, cx);
 1375        assert_eq!(
 1376            editor.selections.display_ranges(cx),
 1377            &[empty_range(1, "ab".len())]
 1378        );
 1379        editor.move_left(&MoveLeft, window, cx);
 1380        assert_eq!(
 1381            editor.selections.display_ranges(cx),
 1382            &[empty_range(1, "a".len())]
 1383        );
 1384
 1385        editor.move_down(&MoveDown, window, cx);
 1386        assert_eq!(
 1387            editor.selections.display_ranges(cx),
 1388            &[empty_range(2, "α".len())]
 1389        );
 1390        editor.move_right(&MoveRight, window, cx);
 1391        assert_eq!(
 1392            editor.selections.display_ranges(cx),
 1393            &[empty_range(2, "αβ".len())]
 1394        );
 1395        editor.move_right(&MoveRight, window, cx);
 1396        assert_eq!(
 1397            editor.selections.display_ranges(cx),
 1398            &[empty_range(2, "αβ⋯".len())]
 1399        );
 1400        editor.move_right(&MoveRight, window, cx);
 1401        assert_eq!(
 1402            editor.selections.display_ranges(cx),
 1403            &[empty_range(2, "αβ⋯ε".len())]
 1404        );
 1405
 1406        editor.move_up(&MoveUp, window, cx);
 1407        assert_eq!(
 1408            editor.selections.display_ranges(cx),
 1409            &[empty_range(1, "ab⋯e".len())]
 1410        );
 1411        editor.move_down(&MoveDown, window, cx);
 1412        assert_eq!(
 1413            editor.selections.display_ranges(cx),
 1414            &[empty_range(2, "αβ⋯ε".len())]
 1415        );
 1416        editor.move_up(&MoveUp, window, cx);
 1417        assert_eq!(
 1418            editor.selections.display_ranges(cx),
 1419            &[empty_range(1, "ab⋯e".len())]
 1420        );
 1421
 1422        editor.move_up(&MoveUp, window, cx);
 1423        assert_eq!(
 1424            editor.selections.display_ranges(cx),
 1425            &[empty_range(0, "🟥🟧".len())]
 1426        );
 1427        editor.move_left(&MoveLeft, window, cx);
 1428        assert_eq!(
 1429            editor.selections.display_ranges(cx),
 1430            &[empty_range(0, "🟥".len())]
 1431        );
 1432        editor.move_left(&MoveLeft, window, cx);
 1433        assert_eq!(
 1434            editor.selections.display_ranges(cx),
 1435            &[empty_range(0, "".len())]
 1436        );
 1437    });
 1438}
 1439
 1440#[gpui::test]
 1441fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1442    init_test(cx, |_| {});
 1443
 1444    let editor = cx.add_window(|window, cx| {
 1445        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1446        build_editor(buffer.clone(), window, cx)
 1447    });
 1448    _ = editor.update(cx, |editor, window, cx| {
 1449        editor.change_selections(None, window, cx, |s| {
 1450            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1451        });
 1452
 1453        // moving above start of document should move selection to start of document,
 1454        // but the next move down should still be at the original goal_x
 1455        editor.move_up(&MoveUp, window, cx);
 1456        assert_eq!(
 1457            editor.selections.display_ranges(cx),
 1458            &[empty_range(0, "".len())]
 1459        );
 1460
 1461        editor.move_down(&MoveDown, window, cx);
 1462        assert_eq!(
 1463            editor.selections.display_ranges(cx),
 1464            &[empty_range(1, "abcd".len())]
 1465        );
 1466
 1467        editor.move_down(&MoveDown, window, cx);
 1468        assert_eq!(
 1469            editor.selections.display_ranges(cx),
 1470            &[empty_range(2, "αβγ".len())]
 1471        );
 1472
 1473        editor.move_down(&MoveDown, window, cx);
 1474        assert_eq!(
 1475            editor.selections.display_ranges(cx),
 1476            &[empty_range(3, "abcd".len())]
 1477        );
 1478
 1479        editor.move_down(&MoveDown, window, cx);
 1480        assert_eq!(
 1481            editor.selections.display_ranges(cx),
 1482            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1483        );
 1484
 1485        // moving past end of document should not change goal_x
 1486        editor.move_down(&MoveDown, window, cx);
 1487        assert_eq!(
 1488            editor.selections.display_ranges(cx),
 1489            &[empty_range(5, "".len())]
 1490        );
 1491
 1492        editor.move_down(&MoveDown, window, cx);
 1493        assert_eq!(
 1494            editor.selections.display_ranges(cx),
 1495            &[empty_range(5, "".len())]
 1496        );
 1497
 1498        editor.move_up(&MoveUp, window, cx);
 1499        assert_eq!(
 1500            editor.selections.display_ranges(cx),
 1501            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1502        );
 1503
 1504        editor.move_up(&MoveUp, window, cx);
 1505        assert_eq!(
 1506            editor.selections.display_ranges(cx),
 1507            &[empty_range(3, "abcd".len())]
 1508        );
 1509
 1510        editor.move_up(&MoveUp, window, cx);
 1511        assert_eq!(
 1512            editor.selections.display_ranges(cx),
 1513            &[empty_range(2, "αβγ".len())]
 1514        );
 1515    });
 1516}
 1517
 1518#[gpui::test]
 1519fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1520    init_test(cx, |_| {});
 1521    let move_to_beg = MoveToBeginningOfLine {
 1522        stop_at_soft_wraps: true,
 1523        stop_at_indent: true,
 1524    };
 1525
 1526    let delete_to_beg = DeleteToBeginningOfLine {
 1527        stop_at_indent: false,
 1528    };
 1529
 1530    let move_to_end = MoveToEndOfLine {
 1531        stop_at_soft_wraps: true,
 1532    };
 1533
 1534    let editor = cx.add_window(|window, cx| {
 1535        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1536        build_editor(buffer, window, cx)
 1537    });
 1538    _ = editor.update(cx, |editor, window, cx| {
 1539        editor.change_selections(None, window, cx, |s| {
 1540            s.select_display_ranges([
 1541                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1542                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1543            ]);
 1544        });
 1545    });
 1546
 1547    _ = editor.update(cx, |editor, window, cx| {
 1548        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1549        assert_eq!(
 1550            editor.selections.display_ranges(cx),
 1551            &[
 1552                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1553                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1554            ]
 1555        );
 1556    });
 1557
 1558    _ = editor.update(cx, |editor, window, cx| {
 1559        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1560        assert_eq!(
 1561            editor.selections.display_ranges(cx),
 1562            &[
 1563                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1564                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1565            ]
 1566        );
 1567    });
 1568
 1569    _ = editor.update(cx, |editor, window, cx| {
 1570        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1571        assert_eq!(
 1572            editor.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1575                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = editor.update(cx, |editor, window, cx| {
 1581        editor.move_to_end_of_line(&move_to_end, window, cx);
 1582        assert_eq!(
 1583            editor.selections.display_ranges(cx),
 1584            &[
 1585                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1586                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1587            ]
 1588        );
 1589    });
 1590
 1591    // Moving to the end of line again is a no-op.
 1592    _ = editor.update(cx, |editor, window, cx| {
 1593        editor.move_to_end_of_line(&move_to_end, window, cx);
 1594        assert_eq!(
 1595            editor.selections.display_ranges(cx),
 1596            &[
 1597                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1598                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1599            ]
 1600        );
 1601    });
 1602
 1603    _ = editor.update(cx, |editor, window, cx| {
 1604        editor.move_left(&MoveLeft, window, cx);
 1605        editor.select_to_beginning_of_line(
 1606            &SelectToBeginningOfLine {
 1607                stop_at_soft_wraps: true,
 1608                stop_at_indent: true,
 1609            },
 1610            window,
 1611            cx,
 1612        );
 1613        assert_eq!(
 1614            editor.selections.display_ranges(cx),
 1615            &[
 1616                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1617                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1618            ]
 1619        );
 1620    });
 1621
 1622    _ = editor.update(cx, |editor, window, cx| {
 1623        editor.select_to_beginning_of_line(
 1624            &SelectToBeginningOfLine {
 1625                stop_at_soft_wraps: true,
 1626                stop_at_indent: true,
 1627            },
 1628            window,
 1629            cx,
 1630        );
 1631        assert_eq!(
 1632            editor.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1636            ]
 1637        );
 1638    });
 1639
 1640    _ = editor.update(cx, |editor, window, cx| {
 1641        editor.select_to_beginning_of_line(
 1642            &SelectToBeginningOfLine {
 1643                stop_at_soft_wraps: true,
 1644                stop_at_indent: 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), 0),
 1653                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1654            ]
 1655        );
 1656    });
 1657
 1658    _ = editor.update(cx, |editor, window, cx| {
 1659        editor.select_to_end_of_line(
 1660            &SelectToEndOfLine {
 1661                stop_at_soft_wraps: true,
 1662            },
 1663            window,
 1664            cx,
 1665        );
 1666        assert_eq!(
 1667            editor.selections.display_ranges(cx),
 1668            &[
 1669                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1670                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1671            ]
 1672        );
 1673    });
 1674
 1675    _ = editor.update(cx, |editor, window, cx| {
 1676        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1677        assert_eq!(editor.display_text(cx), "ab\n  de");
 1678        assert_eq!(
 1679            editor.selections.display_ranges(cx),
 1680            &[
 1681                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1682                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1683            ]
 1684        );
 1685    });
 1686
 1687    _ = editor.update(cx, |editor, window, cx| {
 1688        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1689        assert_eq!(editor.display_text(cx), "\n");
 1690        assert_eq!(
 1691            editor.selections.display_ranges(cx),
 1692            &[
 1693                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1694                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1695            ]
 1696        );
 1697    });
 1698}
 1699
 1700#[gpui::test]
 1701fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1702    init_test(cx, |_| {});
 1703    let move_to_beg = MoveToBeginningOfLine {
 1704        stop_at_soft_wraps: false,
 1705        stop_at_indent: false,
 1706    };
 1707
 1708    let move_to_end = MoveToEndOfLine {
 1709        stop_at_soft_wraps: false,
 1710    };
 1711
 1712    let editor = cx.add_window(|window, cx| {
 1713        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1714        build_editor(buffer, window, cx)
 1715    });
 1716
 1717    _ = editor.update(cx, |editor, window, cx| {
 1718        editor.set_wrap_width(Some(140.0.into()), cx);
 1719
 1720        // We expect the following lines after wrapping
 1721        // ```
 1722        // thequickbrownfox
 1723        // jumpedoverthelazydo
 1724        // gs
 1725        // ```
 1726        // The final `gs` was soft-wrapped onto a new line.
 1727        assert_eq!(
 1728            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1729            editor.display_text(cx),
 1730        );
 1731
 1732        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1733        // Start the cursor at the `k` on the first line
 1734        editor.change_selections(None, window, cx, |s| {
 1735            s.select_display_ranges([
 1736                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1737            ]);
 1738        });
 1739
 1740        // Moving to the beginning of the line should put us at the beginning of the line.
 1741        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1742        assert_eq!(
 1743            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1744            editor.selections.display_ranges(cx)
 1745        );
 1746
 1747        // Moving to the end of the line should put us at the end of the line.
 1748        editor.move_to_end_of_line(&move_to_end, window, cx);
 1749        assert_eq!(
 1750            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1751            editor.selections.display_ranges(cx)
 1752        );
 1753
 1754        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1755        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1756        editor.change_selections(None, window, cx, |s| {
 1757            s.select_display_ranges([
 1758                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1759            ]);
 1760        });
 1761
 1762        // Moving to the beginning of the line should put us at the start of the second line of
 1763        // display text, i.e., the `j`.
 1764        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1765        assert_eq!(
 1766            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1767            editor.selections.display_ranges(cx)
 1768        );
 1769
 1770        // Moving to the beginning of the line again should be a no-op.
 1771        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1772        assert_eq!(
 1773            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1774            editor.selections.display_ranges(cx)
 1775        );
 1776
 1777        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1778        // next display line.
 1779        editor.move_to_end_of_line(&move_to_end, window, cx);
 1780        assert_eq!(
 1781            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1782            editor.selections.display_ranges(cx)
 1783        );
 1784
 1785        // Moving to the end of the line again should be a no-op.
 1786        editor.move_to_end_of_line(&move_to_end, window, cx);
 1787        assert_eq!(
 1788            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1789            editor.selections.display_ranges(cx)
 1790        );
 1791    });
 1792}
 1793
 1794#[gpui::test]
 1795fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1796    init_test(cx, |_| {});
 1797
 1798    let move_to_beg = MoveToBeginningOfLine {
 1799        stop_at_soft_wraps: true,
 1800        stop_at_indent: true,
 1801    };
 1802
 1803    let select_to_beg = SelectToBeginningOfLine {
 1804        stop_at_soft_wraps: true,
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let delete_to_beg = DeleteToBeginningOfLine {
 1809        stop_at_indent: true,
 1810    };
 1811
 1812    let move_to_end = MoveToEndOfLine {
 1813        stop_at_soft_wraps: false,
 1814    };
 1815
 1816    let editor = cx.add_window(|window, cx| {
 1817        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1818        build_editor(buffer, window, cx)
 1819    });
 1820
 1821    _ = editor.update(cx, |editor, window, cx| {
 1822        editor.change_selections(None, window, cx, |s| {
 1823            s.select_display_ranges([
 1824                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1825                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1826            ]);
 1827        });
 1828
 1829        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1830        // and the second cursor at the first non-whitespace character in the line.
 1831        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1832        assert_eq!(
 1833            editor.selections.display_ranges(cx),
 1834            &[
 1835                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1836                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1837            ]
 1838        );
 1839
 1840        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1841        // and should move the second cursor to the beginning of the line.
 1842        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1843        assert_eq!(
 1844            editor.selections.display_ranges(cx),
 1845            &[
 1846                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1847                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1848            ]
 1849        );
 1850
 1851        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1852        // and should move the second cursor back to the first non-whitespace character in the line.
 1853        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1854        assert_eq!(
 1855            editor.selections.display_ranges(cx),
 1856            &[
 1857                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1858                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1859            ]
 1860        );
 1861
 1862        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1863        // and to the first non-whitespace character in the line for the second cursor.
 1864        editor.move_to_end_of_line(&move_to_end, window, cx);
 1865        editor.move_left(&MoveLeft, window, cx);
 1866        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1867        assert_eq!(
 1868            editor.selections.display_ranges(cx),
 1869            &[
 1870                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1871                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1872            ]
 1873        );
 1874
 1875        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1876        // and should select to the beginning of the line for the second cursor.
 1877        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1878        assert_eq!(
 1879            editor.selections.display_ranges(cx),
 1880            &[
 1881                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1882                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1883            ]
 1884        );
 1885
 1886        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1887        // and should delete to the first non-whitespace character in the line for the second cursor.
 1888        editor.move_to_end_of_line(&move_to_end, window, cx);
 1889        editor.move_left(&MoveLeft, window, cx);
 1890        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1891        assert_eq!(editor.text(cx), "c\n  f");
 1892    });
 1893}
 1894
 1895#[gpui::test]
 1896fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1897    init_test(cx, |_| {});
 1898
 1899    let editor = cx.add_window(|window, cx| {
 1900        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1901        build_editor(buffer, window, cx)
 1902    });
 1903    _ = editor.update(cx, |editor, window, cx| {
 1904        editor.change_selections(None, window, cx, |s| {
 1905            s.select_display_ranges([
 1906                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1907                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1908            ])
 1909        });
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1918        assert_selection_ranges("use ˇstd::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1921        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1924        assert_selection_ranges("ˇuse std::str::{foo, ˇbar}\n\n  {baz.qux()}", editor, cx);
 1925
 1926        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1927        assert_selection_ranges("useˇ std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1928
 1929        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1930        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1931
 1932        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1933        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1934
 1935        editor.move_right(&MoveRight, window, cx);
 1936        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1937        assert_selection_ranges(
 1938            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1939            editor,
 1940            cx,
 1941        );
 1942
 1943        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1944        assert_selection_ranges(
 1945            "use std«ˇ::s»tr::{foo, bar}\n\n«ˇ  {b»az.qux()}",
 1946            editor,
 1947            cx,
 1948        );
 1949
 1950        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1951        assert_selection_ranges(
 1952            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1953            editor,
 1954            cx,
 1955        );
 1956    });
 1957}
 1958
 1959#[gpui::test]
 1960fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1961    init_test(cx, |_| {});
 1962
 1963    let editor = cx.add_window(|window, cx| {
 1964        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1965        build_editor(buffer, window, cx)
 1966    });
 1967
 1968    _ = editor.update(cx, |editor, window, cx| {
 1969        editor.set_wrap_width(Some(140.0.into()), cx);
 1970        assert_eq!(
 1971            editor.display_text(cx),
 1972            "use one::{\n    two::three::\n    four::five\n};"
 1973        );
 1974
 1975        editor.change_selections(None, window, cx, |s| {
 1976            s.select_display_ranges([
 1977                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1978            ]);
 1979        });
 1980
 1981        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1982        assert_eq!(
 1983            editor.selections.display_ranges(cx),
 1984            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1985        );
 1986
 1987        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1988        assert_eq!(
 1989            editor.selections.display_ranges(cx),
 1990            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1991        );
 1992
 1993        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1994        assert_eq!(
 1995            editor.selections.display_ranges(cx),
 1996            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1997        );
 1998
 1999        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 2000        assert_eq!(
 2001            editor.selections.display_ranges(cx),
 2002            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2003        );
 2004
 2005        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2006        assert_eq!(
 2007            editor.selections.display_ranges(cx),
 2008            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2009        );
 2010
 2011        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2012        assert_eq!(
 2013            editor.selections.display_ranges(cx),
 2014            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2015        );
 2016    });
 2017}
 2018
 2019#[gpui::test]
 2020async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2021    init_test(cx, |_| {});
 2022    let mut cx = EditorTestContext::new(cx).await;
 2023
 2024    let line_height = cx.editor(|editor, window, _| {
 2025        editor
 2026            .style()
 2027            .unwrap()
 2028            .text
 2029            .line_height_in_pixels(window.rem_size())
 2030    });
 2031    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2032
 2033    cx.set_state(
 2034        &r#"ˇone
 2035        two
 2036
 2037        three
 2038        fourˇ
 2039        five
 2040
 2041        six"#
 2042            .unindent(),
 2043    );
 2044
 2045    cx.update_editor(|editor, window, cx| {
 2046        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2047    });
 2048    cx.assert_editor_state(
 2049        &r#"one
 2050        two
 2051        ˇ
 2052        three
 2053        four
 2054        five
 2055        ˇ
 2056        six"#
 2057            .unindent(),
 2058    );
 2059
 2060    cx.update_editor(|editor, window, cx| {
 2061        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2062    });
 2063    cx.assert_editor_state(
 2064        &r#"one
 2065        two
 2066
 2067        three
 2068        four
 2069        five
 2070        ˇ
 2071        sixˇ"#
 2072            .unindent(),
 2073    );
 2074
 2075    cx.update_editor(|editor, window, cx| {
 2076        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2077    });
 2078    cx.assert_editor_state(
 2079        &r#"one
 2080        two
 2081
 2082        three
 2083        four
 2084        five
 2085
 2086        sixˇ"#
 2087            .unindent(),
 2088    );
 2089
 2090    cx.update_editor(|editor, window, cx| {
 2091        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2092    });
 2093    cx.assert_editor_state(
 2094        &r#"one
 2095        two
 2096
 2097        three
 2098        four
 2099        five
 2100        ˇ
 2101        six"#
 2102            .unindent(),
 2103    );
 2104
 2105    cx.update_editor(|editor, window, cx| {
 2106        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2107    });
 2108    cx.assert_editor_state(
 2109        &r#"one
 2110        two
 2111        ˇ
 2112        three
 2113        four
 2114        five
 2115
 2116        six"#
 2117            .unindent(),
 2118    );
 2119
 2120    cx.update_editor(|editor, window, cx| {
 2121        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2122    });
 2123    cx.assert_editor_state(
 2124        &r#"ˇone
 2125        two
 2126
 2127        three
 2128        four
 2129        five
 2130
 2131        six"#
 2132            .unindent(),
 2133    );
 2134}
 2135
 2136#[gpui::test]
 2137async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2138    init_test(cx, |_| {});
 2139    let mut cx = EditorTestContext::new(cx).await;
 2140    let line_height = cx.editor(|editor, window, _| {
 2141        editor
 2142            .style()
 2143            .unwrap()
 2144            .text
 2145            .line_height_in_pixels(window.rem_size())
 2146    });
 2147    let window = cx.window;
 2148    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2149
 2150    cx.set_state(
 2151        r#"ˇone
 2152        two
 2153        three
 2154        four
 2155        five
 2156        six
 2157        seven
 2158        eight
 2159        nine
 2160        ten
 2161        "#,
 2162    );
 2163
 2164    cx.update_editor(|editor, window, cx| {
 2165        assert_eq!(
 2166            editor.snapshot(window, cx).scroll_position(),
 2167            gpui::Point::new(0., 0.)
 2168        );
 2169        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2170        assert_eq!(
 2171            editor.snapshot(window, cx).scroll_position(),
 2172            gpui::Point::new(0., 3.)
 2173        );
 2174        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2175        assert_eq!(
 2176            editor.snapshot(window, cx).scroll_position(),
 2177            gpui::Point::new(0., 6.)
 2178        );
 2179        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2180        assert_eq!(
 2181            editor.snapshot(window, cx).scroll_position(),
 2182            gpui::Point::new(0., 3.)
 2183        );
 2184
 2185        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2186        assert_eq!(
 2187            editor.snapshot(window, cx).scroll_position(),
 2188            gpui::Point::new(0., 1.)
 2189        );
 2190        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2191        assert_eq!(
 2192            editor.snapshot(window, cx).scroll_position(),
 2193            gpui::Point::new(0., 3.)
 2194        );
 2195    });
 2196}
 2197
 2198#[gpui::test]
 2199async fn test_autoscroll(cx: &mut TestAppContext) {
 2200    init_test(cx, |_| {});
 2201    let mut cx = EditorTestContext::new(cx).await;
 2202
 2203    let line_height = cx.update_editor(|editor, window, cx| {
 2204        editor.set_vertical_scroll_margin(2, cx);
 2205        editor
 2206            .style()
 2207            .unwrap()
 2208            .text
 2209            .line_height_in_pixels(window.rem_size())
 2210    });
 2211    let window = cx.window;
 2212    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2213
 2214    cx.set_state(
 2215        r#"ˇone
 2216            two
 2217            three
 2218            four
 2219            five
 2220            six
 2221            seven
 2222            eight
 2223            nine
 2224            ten
 2225        "#,
 2226    );
 2227    cx.update_editor(|editor, window, cx| {
 2228        assert_eq!(
 2229            editor.snapshot(window, cx).scroll_position(),
 2230            gpui::Point::new(0., 0.0)
 2231        );
 2232    });
 2233
 2234    // Add a cursor below the visible area. Since both cursors cannot fit
 2235    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2236    // allows the vertical scroll margin below that cursor.
 2237    cx.update_editor(|editor, window, cx| {
 2238        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2239            selections.select_ranges([
 2240                Point::new(0, 0)..Point::new(0, 0),
 2241                Point::new(6, 0)..Point::new(6, 0),
 2242            ]);
 2243        })
 2244    });
 2245    cx.update_editor(|editor, window, cx| {
 2246        assert_eq!(
 2247            editor.snapshot(window, cx).scroll_position(),
 2248            gpui::Point::new(0., 3.0)
 2249        );
 2250    });
 2251
 2252    // Move down. The editor cursor scrolls down to track the newest cursor.
 2253    cx.update_editor(|editor, window, cx| {
 2254        editor.move_down(&Default::default(), window, cx);
 2255    });
 2256    cx.update_editor(|editor, window, cx| {
 2257        assert_eq!(
 2258            editor.snapshot(window, cx).scroll_position(),
 2259            gpui::Point::new(0., 4.0)
 2260        );
 2261    });
 2262
 2263    // Add a cursor above the visible area. Since both cursors fit on screen,
 2264    // the editor scrolls to show both.
 2265    cx.update_editor(|editor, window, cx| {
 2266        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2267            selections.select_ranges([
 2268                Point::new(1, 0)..Point::new(1, 0),
 2269                Point::new(6, 0)..Point::new(6, 0),
 2270            ]);
 2271        })
 2272    });
 2273    cx.update_editor(|editor, window, cx| {
 2274        assert_eq!(
 2275            editor.snapshot(window, cx).scroll_position(),
 2276            gpui::Point::new(0., 1.0)
 2277        );
 2278    });
 2279}
 2280
 2281#[gpui::test]
 2282async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2283    init_test(cx, |_| {});
 2284    let mut cx = EditorTestContext::new(cx).await;
 2285
 2286    let line_height = cx.editor(|editor, window, _cx| {
 2287        editor
 2288            .style()
 2289            .unwrap()
 2290            .text
 2291            .line_height_in_pixels(window.rem_size())
 2292    });
 2293    let window = cx.window;
 2294    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2295    cx.set_state(
 2296        &r#"
 2297        ˇone
 2298        two
 2299        threeˇ
 2300        four
 2301        five
 2302        six
 2303        seven
 2304        eight
 2305        nine
 2306        ten
 2307        "#
 2308        .unindent(),
 2309    );
 2310
 2311    cx.update_editor(|editor, window, cx| {
 2312        editor.move_page_down(&MovePageDown::default(), window, cx)
 2313    });
 2314    cx.assert_editor_state(
 2315        &r#"
 2316        one
 2317        two
 2318        three
 2319        ˇfour
 2320        five
 2321        sixˇ
 2322        seven
 2323        eight
 2324        nine
 2325        ten
 2326        "#
 2327        .unindent(),
 2328    );
 2329
 2330    cx.update_editor(|editor, window, cx| {
 2331        editor.move_page_down(&MovePageDown::default(), window, cx)
 2332    });
 2333    cx.assert_editor_state(
 2334        &r#"
 2335        one
 2336        two
 2337        three
 2338        four
 2339        five
 2340        six
 2341        ˇseven
 2342        eight
 2343        nineˇ
 2344        ten
 2345        "#
 2346        .unindent(),
 2347    );
 2348
 2349    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2350    cx.assert_editor_state(
 2351        &r#"
 2352        one
 2353        two
 2354        three
 2355        ˇfour
 2356        five
 2357        sixˇ
 2358        seven
 2359        eight
 2360        nine
 2361        ten
 2362        "#
 2363        .unindent(),
 2364    );
 2365
 2366    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2367    cx.assert_editor_state(
 2368        &r#"
 2369        ˇone
 2370        two
 2371        threeˇ
 2372        four
 2373        five
 2374        six
 2375        seven
 2376        eight
 2377        nine
 2378        ten
 2379        "#
 2380        .unindent(),
 2381    );
 2382
 2383    // Test select collapsing
 2384    cx.update_editor(|editor, window, cx| {
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386        editor.move_page_down(&MovePageDown::default(), window, cx);
 2387        editor.move_page_down(&MovePageDown::default(), window, cx);
 2388    });
 2389    cx.assert_editor_state(
 2390        &r#"
 2391        one
 2392        two
 2393        three
 2394        four
 2395        five
 2396        six
 2397        seven
 2398        eight
 2399        nine
 2400        ˇten
 2401        ˇ"#
 2402        .unindent(),
 2403    );
 2404}
 2405
 2406#[gpui::test]
 2407async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2408    init_test(cx, |_| {});
 2409    let mut cx = EditorTestContext::new(cx).await;
 2410    cx.set_state("one «two threeˇ» four");
 2411    cx.update_editor(|editor, window, cx| {
 2412        editor.delete_to_beginning_of_line(
 2413            &DeleteToBeginningOfLine {
 2414                stop_at_indent: false,
 2415            },
 2416            window,
 2417            cx,
 2418        );
 2419        assert_eq!(editor.text(cx), " four");
 2420    });
 2421}
 2422
 2423#[gpui::test]
 2424fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2425    init_test(cx, |_| {});
 2426
 2427    let editor = cx.add_window(|window, cx| {
 2428        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2429        build_editor(buffer.clone(), window, cx)
 2430    });
 2431
 2432    _ = editor.update(cx, |editor, window, cx| {
 2433        editor.change_selections(None, window, cx, |s| {
 2434            s.select_display_ranges([
 2435                // an empty selection - the preceding word fragment is deleted
 2436                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2437                // characters selected - they are deleted
 2438                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2439            ])
 2440        });
 2441        editor.delete_to_previous_word_start(
 2442            &DeleteToPreviousWordStart {
 2443                ignore_newlines: false,
 2444            },
 2445            window,
 2446            cx,
 2447        );
 2448        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2449    });
 2450
 2451    _ = editor.update(cx, |editor, window, cx| {
 2452        editor.change_selections(None, window, cx, |s| {
 2453            s.select_display_ranges([
 2454                // an empty selection - the following word fragment is deleted
 2455                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2456                // characters selected - they are deleted
 2457                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2458            ])
 2459        });
 2460        editor.delete_to_next_word_end(
 2461            &DeleteToNextWordEnd {
 2462                ignore_newlines: false,
 2463            },
 2464            window,
 2465            cx,
 2466        );
 2467        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2468    });
 2469}
 2470
 2471#[gpui::test]
 2472fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2473    init_test(cx, |_| {});
 2474
 2475    let editor = cx.add_window(|window, cx| {
 2476        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2477        build_editor(buffer.clone(), window, cx)
 2478    });
 2479    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2480        ignore_newlines: false,
 2481    };
 2482    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2483        ignore_newlines: true,
 2484    };
 2485
 2486    _ = editor.update(cx, |editor, window, cx| {
 2487        editor.change_selections(None, window, cx, |s| {
 2488            s.select_display_ranges([
 2489                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2490            ])
 2491        });
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2502        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2503        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2504    });
 2505}
 2506
 2507#[gpui::test]
 2508fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2509    init_test(cx, |_| {});
 2510
 2511    let editor = cx.add_window(|window, cx| {
 2512        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2513        build_editor(buffer.clone(), window, cx)
 2514    });
 2515    let del_to_next_word_end = DeleteToNextWordEnd {
 2516        ignore_newlines: false,
 2517    };
 2518    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2519        ignore_newlines: true,
 2520    };
 2521
 2522    _ = editor.update(cx, |editor, window, cx| {
 2523        editor.change_selections(None, window, cx, |s| {
 2524            s.select_display_ranges([
 2525                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2526            ])
 2527        });
 2528        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2529        assert_eq!(
 2530            editor.buffer.read(cx).read(cx).text(),
 2531            "one\n   two\nthree\n   four"
 2532        );
 2533        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2534        assert_eq!(
 2535            editor.buffer.read(cx).read(cx).text(),
 2536            "\n   two\nthree\n   four"
 2537        );
 2538        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2539        assert_eq!(
 2540            editor.buffer.read(cx).read(cx).text(),
 2541            "two\nthree\n   four"
 2542        );
 2543        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2547        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2548        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2549    });
 2550}
 2551
 2552#[gpui::test]
 2553fn test_newline(cx: &mut TestAppContext) {
 2554    init_test(cx, |_| {});
 2555
 2556    let editor = cx.add_window(|window, cx| {
 2557        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2558        build_editor(buffer.clone(), window, cx)
 2559    });
 2560
 2561    _ = editor.update(cx, |editor, window, cx| {
 2562        editor.change_selections(None, window, cx, |s| {
 2563            s.select_display_ranges([
 2564                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2565                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2566                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2567            ])
 2568        });
 2569
 2570        editor.newline(&Newline, window, cx);
 2571        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2572    });
 2573}
 2574
 2575#[gpui::test]
 2576fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2577    init_test(cx, |_| {});
 2578
 2579    let editor = cx.add_window(|window, cx| {
 2580        let buffer = MultiBuffer::build_simple(
 2581            "
 2582                a
 2583                b(
 2584                    X
 2585                )
 2586                c(
 2587                    X
 2588                )
 2589            "
 2590            .unindent()
 2591            .as_str(),
 2592            cx,
 2593        );
 2594        let mut editor = build_editor(buffer.clone(), window, cx);
 2595        editor.change_selections(None, window, cx, |s| {
 2596            s.select_ranges([
 2597                Point::new(2, 4)..Point::new(2, 5),
 2598                Point::new(5, 4)..Point::new(5, 5),
 2599            ])
 2600        });
 2601        editor
 2602    });
 2603
 2604    _ = editor.update(cx, |editor, window, cx| {
 2605        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2606        editor.buffer.update(cx, |buffer, cx| {
 2607            buffer.edit(
 2608                [
 2609                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2610                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2611                ],
 2612                None,
 2613                cx,
 2614            );
 2615            assert_eq!(
 2616                buffer.read(cx).text(),
 2617                "
 2618                    a
 2619                    b()
 2620                    c()
 2621                "
 2622                .unindent()
 2623            );
 2624        });
 2625        assert_eq!(
 2626            editor.selections.ranges(cx),
 2627            &[
 2628                Point::new(1, 2)..Point::new(1, 2),
 2629                Point::new(2, 2)..Point::new(2, 2),
 2630            ],
 2631        );
 2632
 2633        editor.newline(&Newline, window, cx);
 2634        assert_eq!(
 2635            editor.text(cx),
 2636            "
 2637                a
 2638                b(
 2639                )
 2640                c(
 2641                )
 2642            "
 2643            .unindent()
 2644        );
 2645
 2646        // The selections are moved after the inserted newlines
 2647        assert_eq!(
 2648            editor.selections.ranges(cx),
 2649            &[
 2650                Point::new(2, 0)..Point::new(2, 0),
 2651                Point::new(4, 0)..Point::new(4, 0),
 2652            ],
 2653        );
 2654    });
 2655}
 2656
 2657#[gpui::test]
 2658async fn test_newline_above(cx: &mut TestAppContext) {
 2659    init_test(cx, |settings| {
 2660        settings.defaults.tab_size = NonZeroU32::new(4)
 2661    });
 2662
 2663    let language = Arc::new(
 2664        Language::new(
 2665            LanguageConfig::default(),
 2666            Some(tree_sitter_rust::LANGUAGE.into()),
 2667        )
 2668        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2669        .unwrap(),
 2670    );
 2671
 2672    let mut cx = EditorTestContext::new(cx).await;
 2673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2674    cx.set_state(indoc! {"
 2675        const a: ˇA = (
 2676 2677                «const_functionˇ»(ˇ),
 2678                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2679 2680        ˇ);ˇ
 2681    "});
 2682
 2683    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2684    cx.assert_editor_state(indoc! {"
 2685        ˇ
 2686        const a: A = (
 2687            ˇ
 2688            (
 2689                ˇ
 2690                ˇ
 2691                const_function(),
 2692                ˇ
 2693                ˇ
 2694                ˇ
 2695                ˇ
 2696                something_else,
 2697                ˇ
 2698            )
 2699            ˇ
 2700            ˇ
 2701        );
 2702    "});
 2703}
 2704
 2705#[gpui::test]
 2706async fn test_newline_below(cx: &mut TestAppContext) {
 2707    init_test(cx, |settings| {
 2708        settings.defaults.tab_size = NonZeroU32::new(4)
 2709    });
 2710
 2711    let language = Arc::new(
 2712        Language::new(
 2713            LanguageConfig::default(),
 2714            Some(tree_sitter_rust::LANGUAGE.into()),
 2715        )
 2716        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2717        .unwrap(),
 2718    );
 2719
 2720    let mut cx = EditorTestContext::new(cx).await;
 2721    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2722    cx.set_state(indoc! {"
 2723        const a: ˇA = (
 2724 2725                «const_functionˇ»(ˇ),
 2726                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2727 2728        ˇ);ˇ
 2729    "});
 2730
 2731    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2732    cx.assert_editor_state(indoc! {"
 2733        const a: A = (
 2734            ˇ
 2735            (
 2736                ˇ
 2737                const_function(),
 2738                ˇ
 2739                ˇ
 2740                something_else,
 2741                ˇ
 2742                ˇ
 2743                ˇ
 2744                ˇ
 2745            )
 2746            ˇ
 2747        );
 2748        ˇ
 2749        ˇ
 2750    "});
 2751}
 2752
 2753#[gpui::test]
 2754async fn test_newline_comments(cx: &mut TestAppContext) {
 2755    init_test(cx, |settings| {
 2756        settings.defaults.tab_size = NonZeroU32::new(4)
 2757    });
 2758
 2759    let language = Arc::new(Language::new(
 2760        LanguageConfig {
 2761            line_comments: vec!["// ".into()],
 2762            ..LanguageConfig::default()
 2763        },
 2764        None,
 2765    ));
 2766    {
 2767        let mut cx = EditorTestContext::new(cx).await;
 2768        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2769        cx.set_state(indoc! {"
 2770        // Fooˇ
 2771    "});
 2772
 2773        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2774        cx.assert_editor_state(indoc! {"
 2775        // Foo
 2776        // ˇ
 2777    "});
 2778        // Ensure that we add comment prefix when existing line contains space
 2779        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2780        cx.assert_editor_state(
 2781            indoc! {"
 2782        // Foo
 2783        //s
 2784        // ˇ
 2785    "}
 2786            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2787            .as_str(),
 2788        );
 2789        // Ensure that we add comment prefix when existing line does not contain space
 2790        cx.set_state(indoc! {"
 2791        // Foo
 2792        //ˇ
 2793    "});
 2794        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795        cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        //
 2798        // ˇ
 2799    "});
 2800        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2801        cx.set_state(indoc! {"
 2802        ˇ// Foo
 2803    "});
 2804        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2805        cx.assert_editor_state(indoc! {"
 2806
 2807        ˇ// Foo
 2808    "});
 2809    }
 2810    // Ensure that comment continuations can be disabled.
 2811    update_test_language_settings(cx, |settings| {
 2812        settings.defaults.extend_comment_on_newline = Some(false);
 2813    });
 2814    let mut cx = EditorTestContext::new(cx).await;
 2815    cx.set_state(indoc! {"
 2816        // Fooˇ
 2817    "});
 2818    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2819    cx.assert_editor_state(indoc! {"
 2820        // Foo
 2821        ˇ
 2822    "});
 2823}
 2824
 2825#[gpui::test]
 2826async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext) {
 2827    init_test(cx, |settings| {
 2828        settings.defaults.tab_size = NonZeroU32::new(4)
 2829    });
 2830
 2831    let language = Arc::new(Language::new(
 2832        LanguageConfig {
 2833            line_comments: vec!["// ".into(), "/// ".into()],
 2834            ..LanguageConfig::default()
 2835        },
 2836        None,
 2837    ));
 2838    {
 2839        let mut cx = EditorTestContext::new(cx).await;
 2840        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2841        cx.set_state(indoc! {"
 2842        //ˇ
 2843    "});
 2844        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2845        cx.assert_editor_state(indoc! {"
 2846        //
 2847        // ˇ
 2848    "});
 2849
 2850        cx.set_state(indoc! {"
 2851        ///ˇ
 2852    "});
 2853        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2854        cx.assert_editor_state(indoc! {"
 2855        ///
 2856        /// ˇ
 2857    "});
 2858    }
 2859}
 2860
 2861#[gpui::test]
 2862async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 2863    init_test(cx, |settings| {
 2864        settings.defaults.tab_size = NonZeroU32::new(4)
 2865    });
 2866
 2867    let language = Arc::new(
 2868        Language::new(
 2869            LanguageConfig {
 2870                documentation: Some(language::DocumentationConfig {
 2871                    start: "/**".into(),
 2872                    end: "*/".into(),
 2873                    prefix: "* ".into(),
 2874                    tab_size: NonZeroU32::new(1).unwrap(),
 2875                }),
 2876
 2877                ..LanguageConfig::default()
 2878            },
 2879            Some(tree_sitter_rust::LANGUAGE.into()),
 2880        )
 2881        .with_override_query("[(line_comment)(block_comment)] @comment.inclusive")
 2882        .unwrap(),
 2883    );
 2884
 2885    {
 2886        let mut cx = EditorTestContext::new(cx).await;
 2887        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2888        cx.set_state(indoc! {"
 2889        /**ˇ
 2890    "});
 2891
 2892        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2893        cx.assert_editor_state(indoc! {"
 2894        /**
 2895         * ˇ
 2896    "});
 2897        // Ensure that if cursor is before the comment start,
 2898        // we do not actually insert a comment prefix.
 2899        cx.set_state(indoc! {"
 2900        ˇ/**
 2901    "});
 2902        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2903        cx.assert_editor_state(indoc! {"
 2904
 2905        ˇ/**
 2906    "});
 2907        // Ensure that if cursor is between it doesn't add comment prefix.
 2908        cx.set_state(indoc! {"
 2909        /*ˇ*
 2910    "});
 2911        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2912        cx.assert_editor_state(indoc! {"
 2913        /*
 2914        ˇ*
 2915    "});
 2916        // Ensure that if suffix exists on same line after cursor it adds new line.
 2917        cx.set_state(indoc! {"
 2918        /**ˇ*/
 2919    "});
 2920        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2921        cx.assert_editor_state(indoc! {"
 2922        /**
 2923         * ˇ
 2924         */
 2925    "});
 2926        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2927        cx.set_state(indoc! {"
 2928        /**ˇ */
 2929    "});
 2930        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2931        cx.assert_editor_state(indoc! {"
 2932        /**
 2933         * ˇ
 2934         */
 2935    "});
 2936        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2937        cx.set_state(indoc! {"
 2938        /** ˇ*/
 2939    "});
 2940        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2941        cx.assert_editor_state(
 2942            indoc! {"
 2943        /**s
 2944         * ˇ
 2945         */
 2946    "}
 2947            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2948            .as_str(),
 2949        );
 2950        // Ensure that delimiter space is preserved when newline on already
 2951        // spaced delimiter.
 2952        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2953        cx.assert_editor_state(
 2954            indoc! {"
 2955        /**s
 2956         *s
 2957         * ˇ
 2958         */
 2959    "}
 2960            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2961            .as_str(),
 2962        );
 2963        // Ensure that delimiter space is preserved when space is not
 2964        // on existing delimiter.
 2965        cx.set_state(indoc! {"
 2966        /**
 2967 2968         */
 2969    "});
 2970        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2971        cx.assert_editor_state(indoc! {"
 2972        /**
 2973         *
 2974         * ˇ
 2975         */
 2976    "});
 2977        // Ensure that if suffix exists on same line after cursor it
 2978        // doesn't add extra new line if prefix is not on same line.
 2979        cx.set_state(indoc! {"
 2980        /**
 2981        ˇ*/
 2982    "});
 2983        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2984        cx.assert_editor_state(indoc! {"
 2985        /**
 2986
 2987        ˇ*/
 2988    "});
 2989        // Ensure that it detects suffix after existing prefix.
 2990        cx.set_state(indoc! {"
 2991        /**ˇ/
 2992    "});
 2993        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2994        cx.assert_editor_state(indoc! {"
 2995        /**
 2996        ˇ/
 2997    "});
 2998        // Ensure that if suffix exists on same line before
 2999        // cursor it does not add comment prefix.
 3000        cx.set_state(indoc! {"
 3001        /** */ˇ
 3002    "});
 3003        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3004        cx.assert_editor_state(indoc! {"
 3005        /** */
 3006        ˇ
 3007    "});
 3008        // Ensure that if suffix exists on same line before
 3009        // cursor it does not add comment prefix.
 3010        cx.set_state(indoc! {"
 3011        /**
 3012         *
 3013         */ˇ
 3014    "});
 3015        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3016        cx.assert_editor_state(indoc! {"
 3017        /**
 3018         *
 3019         */
 3020         ˇ
 3021    "});
 3022
 3023        // Ensure that inline comment followed by code
 3024        // doesn't add comment prefix on newline
 3025        cx.set_state(indoc! {"
 3026        /** */ textˇ
 3027    "});
 3028        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3029        cx.assert_editor_state(indoc! {"
 3030        /** */ text
 3031        ˇ
 3032    "});
 3033
 3034        // Ensure that text after comment end tag
 3035        // doesn't add comment prefix on newline
 3036        cx.set_state(indoc! {"
 3037        /**
 3038         *
 3039         */ˇtext
 3040    "});
 3041        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3042        cx.assert_editor_state(indoc! {"
 3043        /**
 3044         *
 3045         */
 3046         ˇtext
 3047    "});
 3048
 3049        // Ensure if not comment block it doesn't
 3050        // add comment prefix on newline
 3051        cx.set_state(indoc! {"
 3052        * textˇ
 3053    "});
 3054        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3055        cx.assert_editor_state(indoc! {"
 3056        * text
 3057        ˇ
 3058    "});
 3059    }
 3060    // Ensure that comment continuations can be disabled.
 3061    update_test_language_settings(cx, |settings| {
 3062        settings.defaults.extend_comment_on_newline = Some(false);
 3063    });
 3064    let mut cx = EditorTestContext::new(cx).await;
 3065    cx.set_state(indoc! {"
 3066        /**ˇ
 3067    "});
 3068    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3069    cx.assert_editor_state(indoc! {"
 3070        /**
 3071        ˇ
 3072    "});
 3073}
 3074
 3075#[gpui::test]
 3076fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 3077    init_test(cx, |_| {});
 3078
 3079    let editor = cx.add_window(|window, cx| {
 3080        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3081        let mut editor = build_editor(buffer.clone(), window, cx);
 3082        editor.change_selections(None, window, cx, |s| {
 3083            s.select_ranges([3..4, 11..12, 19..20])
 3084        });
 3085        editor
 3086    });
 3087
 3088    _ = editor.update(cx, |editor, window, cx| {
 3089        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3090        editor.buffer.update(cx, |buffer, cx| {
 3091            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3092            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3093        });
 3094        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3095
 3096        editor.insert("Z", window, cx);
 3097        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3098
 3099        // The selections are moved after the inserted characters
 3100        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3101    });
 3102}
 3103
 3104#[gpui::test]
 3105async fn test_tab(cx: &mut TestAppContext) {
 3106    init_test(cx, |settings| {
 3107        settings.defaults.tab_size = NonZeroU32::new(3)
 3108    });
 3109
 3110    let mut cx = EditorTestContext::new(cx).await;
 3111    cx.set_state(indoc! {"
 3112        ˇabˇc
 3113        ˇ🏀ˇ🏀ˇefg
 3114 3115    "});
 3116    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3117    cx.assert_editor_state(indoc! {"
 3118           ˇab ˇc
 3119           ˇ🏀  ˇ🏀  ˇefg
 3120        d  ˇ
 3121    "});
 3122
 3123    cx.set_state(indoc! {"
 3124        a
 3125        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        a
 3130           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3131    "});
 3132}
 3133
 3134#[gpui::test]
 3135async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3136    init_test(cx, |_| {});
 3137
 3138    let mut cx = EditorTestContext::new(cx).await;
 3139    let language = Arc::new(
 3140        Language::new(
 3141            LanguageConfig::default(),
 3142            Some(tree_sitter_rust::LANGUAGE.into()),
 3143        )
 3144        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3145        .unwrap(),
 3146    );
 3147    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3148
 3149    // test when all cursors are not at suggested indent
 3150    // then simply move to their suggested indent location
 3151    cx.set_state(indoc! {"
 3152        const a: B = (
 3153            c(
 3154        ˇ
 3155        ˇ    )
 3156        );
 3157    "});
 3158    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3159    cx.assert_editor_state(indoc! {"
 3160        const a: B = (
 3161            c(
 3162                ˇ
 3163            ˇ)
 3164        );
 3165    "});
 3166
 3167    // test cursor already at suggested indent not moving when
 3168    // other cursors are yet to reach their suggested indents
 3169    cx.set_state(indoc! {"
 3170        ˇ
 3171        const a: B = (
 3172            c(
 3173                d(
 3174        ˇ
 3175                )
 3176        ˇ
 3177        ˇ    )
 3178        );
 3179    "});
 3180    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        ˇ
 3183        const a: B = (
 3184            c(
 3185                d(
 3186                    ˇ
 3187                )
 3188                ˇ
 3189            ˇ)
 3190        );
 3191    "});
 3192    // test when all cursors are at suggested indent then tab is inserted
 3193    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3194    cx.assert_editor_state(indoc! {"
 3195            ˇ
 3196        const a: B = (
 3197            c(
 3198                d(
 3199                        ˇ
 3200                )
 3201                    ˇ
 3202                ˇ)
 3203        );
 3204    "});
 3205
 3206    // test when current indent is less than suggested indent,
 3207    // we adjust line to match suggested indent and move cursor to it
 3208    //
 3209    // when no other cursor is at word boundary, all of them should move
 3210    cx.set_state(indoc! {"
 3211        const a: B = (
 3212            c(
 3213                d(
 3214        ˇ
 3215        ˇ   )
 3216        ˇ   )
 3217        );
 3218    "});
 3219    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3220    cx.assert_editor_state(indoc! {"
 3221        const a: B = (
 3222            c(
 3223                d(
 3224                    ˇ
 3225                ˇ)
 3226            ˇ)
 3227        );
 3228    "});
 3229
 3230    // test when current indent is less than suggested indent,
 3231    // we adjust line to match suggested indent and move cursor to it
 3232    //
 3233    // when some other cursor is at word boundary, it should not move
 3234    cx.set_state(indoc! {"
 3235        const a: B = (
 3236            c(
 3237                d(
 3238        ˇ
 3239        ˇ   )
 3240           ˇ)
 3241        );
 3242    "});
 3243    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3244    cx.assert_editor_state(indoc! {"
 3245        const a: B = (
 3246            c(
 3247                d(
 3248                    ˇ
 3249                ˇ)
 3250            ˇ)
 3251        );
 3252    "});
 3253
 3254    // test when current indent is more than suggested indent,
 3255    // we just move cursor to current indent instead of suggested indent
 3256    //
 3257    // when no other cursor is at word boundary, all of them should move
 3258    cx.set_state(indoc! {"
 3259        const a: B = (
 3260            c(
 3261                d(
 3262        ˇ
 3263        ˇ                )
 3264        ˇ   )
 3265        );
 3266    "});
 3267    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        const a: B = (
 3270            c(
 3271                d(
 3272                    ˇ
 3273                        ˇ)
 3274            ˇ)
 3275        );
 3276    "});
 3277    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3278    cx.assert_editor_state(indoc! {"
 3279        const a: B = (
 3280            c(
 3281                d(
 3282                        ˇ
 3283                            ˇ)
 3284                ˇ)
 3285        );
 3286    "});
 3287
 3288    // test when current indent is more than suggested indent,
 3289    // we just move cursor to current indent instead of suggested indent
 3290    //
 3291    // when some other cursor is at word boundary, it doesn't move
 3292    cx.set_state(indoc! {"
 3293        const a: B = (
 3294            c(
 3295                d(
 3296        ˇ
 3297        ˇ                )
 3298            ˇ)
 3299        );
 3300    "});
 3301    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        const a: B = (
 3304            c(
 3305                d(
 3306                    ˇ
 3307                        ˇ)
 3308            ˇ)
 3309        );
 3310    "});
 3311
 3312    // handle auto-indent when there are multiple cursors on the same line
 3313    cx.set_state(indoc! {"
 3314        const a: B = (
 3315            c(
 3316        ˇ    ˇ
 3317        ˇ    )
 3318        );
 3319    "});
 3320    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3321    cx.assert_editor_state(indoc! {"
 3322        const a: B = (
 3323            c(
 3324                ˇ
 3325            ˇ)
 3326        );
 3327    "});
 3328}
 3329
 3330#[gpui::test]
 3331async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3332    init_test(cx, |settings| {
 3333        settings.defaults.tab_size = NonZeroU32::new(3)
 3334    });
 3335
 3336    let mut cx = EditorTestContext::new(cx).await;
 3337    cx.set_state(indoc! {"
 3338         ˇ
 3339        \t ˇ
 3340        \t  ˇ
 3341        \t   ˇ
 3342         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3343    "});
 3344
 3345    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3346    cx.assert_editor_state(indoc! {"
 3347           ˇ
 3348        \t   ˇ
 3349        \t   ˇ
 3350        \t      ˇ
 3351         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3352    "});
 3353}
 3354
 3355#[gpui::test]
 3356async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3357    init_test(cx, |settings| {
 3358        settings.defaults.tab_size = NonZeroU32::new(4)
 3359    });
 3360
 3361    let language = Arc::new(
 3362        Language::new(
 3363            LanguageConfig::default(),
 3364            Some(tree_sitter_rust::LANGUAGE.into()),
 3365        )
 3366        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3367        .unwrap(),
 3368    );
 3369
 3370    let mut cx = EditorTestContext::new(cx).await;
 3371    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3372    cx.set_state(indoc! {"
 3373        fn a() {
 3374            if b {
 3375        \t ˇc
 3376            }
 3377        }
 3378    "});
 3379
 3380    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3381    cx.assert_editor_state(indoc! {"
 3382        fn a() {
 3383            if b {
 3384                ˇc
 3385            }
 3386        }
 3387    "});
 3388}
 3389
 3390#[gpui::test]
 3391async fn test_indent_outdent(cx: &mut TestAppContext) {
 3392    init_test(cx, |settings| {
 3393        settings.defaults.tab_size = NonZeroU32::new(4);
 3394    });
 3395
 3396    let mut cx = EditorTestContext::new(cx).await;
 3397
 3398    cx.set_state(indoc! {"
 3399          «oneˇ» «twoˇ»
 3400        three
 3401         four
 3402    "});
 3403    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3404    cx.assert_editor_state(indoc! {"
 3405            «oneˇ» «twoˇ»
 3406        three
 3407         four
 3408    "});
 3409
 3410    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3411    cx.assert_editor_state(indoc! {"
 3412        «oneˇ» «twoˇ»
 3413        three
 3414         four
 3415    "});
 3416
 3417    // select across line ending
 3418    cx.set_state(indoc! {"
 3419        one two
 3420        t«hree
 3421        ˇ» four
 3422    "});
 3423    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3424    cx.assert_editor_state(indoc! {"
 3425        one two
 3426            t«hree
 3427        ˇ» four
 3428    "});
 3429
 3430    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3431    cx.assert_editor_state(indoc! {"
 3432        one two
 3433        t«hree
 3434        ˇ» four
 3435    "});
 3436
 3437    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3438    cx.set_state(indoc! {"
 3439        one two
 3440        ˇthree
 3441            four
 3442    "});
 3443    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3444    cx.assert_editor_state(indoc! {"
 3445        one two
 3446            ˇthree
 3447            four
 3448    "});
 3449
 3450    cx.set_state(indoc! {"
 3451        one two
 3452        ˇ    three
 3453            four
 3454    "});
 3455    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3456    cx.assert_editor_state(indoc! {"
 3457        one two
 3458        ˇthree
 3459            four
 3460    "});
 3461}
 3462
 3463#[gpui::test]
 3464async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3465    init_test(cx, |settings| {
 3466        settings.defaults.hard_tabs = Some(true);
 3467    });
 3468
 3469    let mut cx = EditorTestContext::new(cx).await;
 3470
 3471    // select two ranges on one line
 3472    cx.set_state(indoc! {"
 3473        «oneˇ» «twoˇ»
 3474        three
 3475        four
 3476    "});
 3477    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3478    cx.assert_editor_state(indoc! {"
 3479        \t«oneˇ» «twoˇ»
 3480        three
 3481        four
 3482    "});
 3483    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3484    cx.assert_editor_state(indoc! {"
 3485        \t\t«oneˇ» «twoˇ»
 3486        three
 3487        four
 3488    "});
 3489    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        \t«oneˇ» «twoˇ»
 3492        three
 3493        four
 3494    "});
 3495    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3496    cx.assert_editor_state(indoc! {"
 3497        «oneˇ» «twoˇ»
 3498        three
 3499        four
 3500    "});
 3501
 3502    // select across a line ending
 3503    cx.set_state(indoc! {"
 3504        one two
 3505        t«hree
 3506        ˇ»four
 3507    "});
 3508    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3509    cx.assert_editor_state(indoc! {"
 3510        one two
 3511        \tt«hree
 3512        ˇ»four
 3513    "});
 3514    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        one two
 3517        \t\tt«hree
 3518        ˇ»four
 3519    "});
 3520    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3521    cx.assert_editor_state(indoc! {"
 3522        one two
 3523        \tt«hree
 3524        ˇ»four
 3525    "});
 3526    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3527    cx.assert_editor_state(indoc! {"
 3528        one two
 3529        t«hree
 3530        ˇ»four
 3531    "});
 3532
 3533    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3534    cx.set_state(indoc! {"
 3535        one two
 3536        ˇthree
 3537        four
 3538    "});
 3539    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3540    cx.assert_editor_state(indoc! {"
 3541        one two
 3542        ˇthree
 3543        four
 3544    "});
 3545    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        one two
 3548        \tˇthree
 3549        four
 3550    "});
 3551    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3552    cx.assert_editor_state(indoc! {"
 3553        one two
 3554        ˇthree
 3555        four
 3556    "});
 3557}
 3558
 3559#[gpui::test]
 3560fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3561    init_test(cx, |settings| {
 3562        settings.languages.extend([
 3563            (
 3564                "TOML".into(),
 3565                LanguageSettingsContent {
 3566                    tab_size: NonZeroU32::new(2),
 3567                    ..Default::default()
 3568                },
 3569            ),
 3570            (
 3571                "Rust".into(),
 3572                LanguageSettingsContent {
 3573                    tab_size: NonZeroU32::new(4),
 3574                    ..Default::default()
 3575                },
 3576            ),
 3577        ]);
 3578    });
 3579
 3580    let toml_language = Arc::new(Language::new(
 3581        LanguageConfig {
 3582            name: "TOML".into(),
 3583            ..Default::default()
 3584        },
 3585        None,
 3586    ));
 3587    let rust_language = Arc::new(Language::new(
 3588        LanguageConfig {
 3589            name: "Rust".into(),
 3590            ..Default::default()
 3591        },
 3592        None,
 3593    ));
 3594
 3595    let toml_buffer =
 3596        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3597    let rust_buffer =
 3598        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3599    let multibuffer = cx.new(|cx| {
 3600        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3601        multibuffer.push_excerpts(
 3602            toml_buffer.clone(),
 3603            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3604            cx,
 3605        );
 3606        multibuffer.push_excerpts(
 3607            rust_buffer.clone(),
 3608            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3609            cx,
 3610        );
 3611        multibuffer
 3612    });
 3613
 3614    cx.add_window(|window, cx| {
 3615        let mut editor = build_editor(multibuffer, window, cx);
 3616
 3617        assert_eq!(
 3618            editor.text(cx),
 3619            indoc! {"
 3620                a = 1
 3621                b = 2
 3622
 3623                const c: usize = 3;
 3624            "}
 3625        );
 3626
 3627        select_ranges(
 3628            &mut editor,
 3629            indoc! {"
 3630                «aˇ» = 1
 3631                b = 2
 3632
 3633                «const c:ˇ» usize = 3;
 3634            "},
 3635            window,
 3636            cx,
 3637        );
 3638
 3639        editor.tab(&Tab, window, cx);
 3640        assert_text_with_selections(
 3641            &mut editor,
 3642            indoc! {"
 3643                  «aˇ» = 1
 3644                b = 2
 3645
 3646                    «const c:ˇ» usize = 3;
 3647            "},
 3648            cx,
 3649        );
 3650        editor.backtab(&Backtab, window, cx);
 3651        assert_text_with_selections(
 3652            &mut editor,
 3653            indoc! {"
 3654                «aˇ» = 1
 3655                b = 2
 3656
 3657                «const c:ˇ» usize = 3;
 3658            "},
 3659            cx,
 3660        );
 3661
 3662        editor
 3663    });
 3664}
 3665
 3666#[gpui::test]
 3667async fn test_backspace(cx: &mut TestAppContext) {
 3668    init_test(cx, |_| {});
 3669
 3670    let mut cx = EditorTestContext::new(cx).await;
 3671
 3672    // Basic backspace
 3673    cx.set_state(indoc! {"
 3674        onˇe two three
 3675        fou«rˇ» five six
 3676        seven «ˇeight nine
 3677        »ten
 3678    "});
 3679    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3680    cx.assert_editor_state(indoc! {"
 3681        oˇe two three
 3682        fouˇ five six
 3683        seven ˇten
 3684    "});
 3685
 3686    // Test backspace inside and around indents
 3687    cx.set_state(indoc! {"
 3688        zero
 3689            ˇone
 3690                ˇtwo
 3691            ˇ ˇ ˇ  three
 3692        ˇ  ˇ  four
 3693    "});
 3694    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3695    cx.assert_editor_state(indoc! {"
 3696        zero
 3697        ˇone
 3698            ˇtwo
 3699        ˇ  threeˇ  four
 3700    "});
 3701}
 3702
 3703#[gpui::test]
 3704async fn test_delete(cx: &mut TestAppContext) {
 3705    init_test(cx, |_| {});
 3706
 3707    let mut cx = EditorTestContext::new(cx).await;
 3708    cx.set_state(indoc! {"
 3709        onˇe two three
 3710        fou«rˇ» five six
 3711        seven «ˇeight nine
 3712        »ten
 3713    "});
 3714    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3715    cx.assert_editor_state(indoc! {"
 3716        onˇ two three
 3717        fouˇ five six
 3718        seven ˇten
 3719    "});
 3720}
 3721
 3722#[gpui::test]
 3723fn test_delete_line(cx: &mut TestAppContext) {
 3724    init_test(cx, |_| {});
 3725
 3726    let editor = cx.add_window(|window, cx| {
 3727        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3728        build_editor(buffer, window, cx)
 3729    });
 3730    _ = editor.update(cx, |editor, window, cx| {
 3731        editor.change_selections(None, window, cx, |s| {
 3732            s.select_display_ranges([
 3733                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3734                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3735                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3736            ])
 3737        });
 3738        editor.delete_line(&DeleteLine, window, cx);
 3739        assert_eq!(editor.display_text(cx), "ghi");
 3740        assert_eq!(
 3741            editor.selections.display_ranges(cx),
 3742            vec![
 3743                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3744                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3745            ]
 3746        );
 3747    });
 3748
 3749    let editor = cx.add_window(|window, cx| {
 3750        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3751        build_editor(buffer, window, cx)
 3752    });
 3753    _ = editor.update(cx, |editor, window, cx| {
 3754        editor.change_selections(None, window, cx, |s| {
 3755            s.select_display_ranges([
 3756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3757            ])
 3758        });
 3759        editor.delete_line(&DeleteLine, window, cx);
 3760        assert_eq!(editor.display_text(cx), "ghi\n");
 3761        assert_eq!(
 3762            editor.selections.display_ranges(cx),
 3763            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3764        );
 3765    });
 3766}
 3767
 3768#[gpui::test]
 3769fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3770    init_test(cx, |_| {});
 3771
 3772    cx.add_window(|window, cx| {
 3773        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3774        let mut editor = build_editor(buffer.clone(), window, cx);
 3775        let buffer = buffer.read(cx).as_singleton().unwrap();
 3776
 3777        assert_eq!(
 3778            editor.selections.ranges::<Point>(cx),
 3779            &[Point::new(0, 0)..Point::new(0, 0)]
 3780        );
 3781
 3782        // When on single line, replace newline at end by space
 3783        editor.join_lines(&JoinLines, window, cx);
 3784        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3785        assert_eq!(
 3786            editor.selections.ranges::<Point>(cx),
 3787            &[Point::new(0, 3)..Point::new(0, 3)]
 3788        );
 3789
 3790        // When multiple lines are selected, remove newlines that are spanned by the selection
 3791        editor.change_selections(None, window, cx, |s| {
 3792            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3793        });
 3794        editor.join_lines(&JoinLines, window, cx);
 3795        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3796        assert_eq!(
 3797            editor.selections.ranges::<Point>(cx),
 3798            &[Point::new(0, 11)..Point::new(0, 11)]
 3799        );
 3800
 3801        // Undo should be transactional
 3802        editor.undo(&Undo, window, cx);
 3803        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3804        assert_eq!(
 3805            editor.selections.ranges::<Point>(cx),
 3806            &[Point::new(0, 5)..Point::new(2, 2)]
 3807        );
 3808
 3809        // When joining an empty line don't insert a space
 3810        editor.change_selections(None, window, cx, |s| {
 3811            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3812        });
 3813        editor.join_lines(&JoinLines, window, cx);
 3814        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3815        assert_eq!(
 3816            editor.selections.ranges::<Point>(cx),
 3817            [Point::new(2, 3)..Point::new(2, 3)]
 3818        );
 3819
 3820        // We can remove trailing newlines
 3821        editor.join_lines(&JoinLines, window, cx);
 3822        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3823        assert_eq!(
 3824            editor.selections.ranges::<Point>(cx),
 3825            [Point::new(2, 3)..Point::new(2, 3)]
 3826        );
 3827
 3828        // We don't blow up on the last line
 3829        editor.join_lines(&JoinLines, window, cx);
 3830        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3831        assert_eq!(
 3832            editor.selections.ranges::<Point>(cx),
 3833            [Point::new(2, 3)..Point::new(2, 3)]
 3834        );
 3835
 3836        // reset to test indentation
 3837        editor.buffer.update(cx, |buffer, cx| {
 3838            buffer.edit(
 3839                [
 3840                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3841                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3842                ],
 3843                None,
 3844                cx,
 3845            )
 3846        });
 3847
 3848        // We remove any leading spaces
 3849        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3850        editor.change_selections(None, window, cx, |s| {
 3851            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3852        });
 3853        editor.join_lines(&JoinLines, window, cx);
 3854        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3855
 3856        // We don't insert a space for a line containing only spaces
 3857        editor.join_lines(&JoinLines, window, cx);
 3858        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3859
 3860        // We ignore any leading tabs
 3861        editor.join_lines(&JoinLines, window, cx);
 3862        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3863
 3864        editor
 3865    });
 3866}
 3867
 3868#[gpui::test]
 3869fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3870    init_test(cx, |_| {});
 3871
 3872    cx.add_window(|window, cx| {
 3873        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3874        let mut editor = build_editor(buffer.clone(), window, cx);
 3875        let buffer = buffer.read(cx).as_singleton().unwrap();
 3876
 3877        editor.change_selections(None, window, cx, |s| {
 3878            s.select_ranges([
 3879                Point::new(0, 2)..Point::new(1, 1),
 3880                Point::new(1, 2)..Point::new(1, 2),
 3881                Point::new(3, 1)..Point::new(3, 2),
 3882            ])
 3883        });
 3884
 3885        editor.join_lines(&JoinLines, window, cx);
 3886        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3887
 3888        assert_eq!(
 3889            editor.selections.ranges::<Point>(cx),
 3890            [
 3891                Point::new(0, 7)..Point::new(0, 7),
 3892                Point::new(1, 3)..Point::new(1, 3)
 3893            ]
 3894        );
 3895        editor
 3896    });
 3897}
 3898
 3899#[gpui::test]
 3900async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3901    init_test(cx, |_| {});
 3902
 3903    let mut cx = EditorTestContext::new(cx).await;
 3904
 3905    let diff_base = r#"
 3906        Line 0
 3907        Line 1
 3908        Line 2
 3909        Line 3
 3910        "#
 3911    .unindent();
 3912
 3913    cx.set_state(
 3914        &r#"
 3915        ˇLine 0
 3916        Line 1
 3917        Line 2
 3918        Line 3
 3919        "#
 3920        .unindent(),
 3921    );
 3922
 3923    cx.set_head_text(&diff_base);
 3924    executor.run_until_parked();
 3925
 3926    // Join lines
 3927    cx.update_editor(|editor, window, cx| {
 3928        editor.join_lines(&JoinLines, window, cx);
 3929    });
 3930    executor.run_until_parked();
 3931
 3932    cx.assert_editor_state(
 3933        &r#"
 3934        Line 0ˇ Line 1
 3935        Line 2
 3936        Line 3
 3937        "#
 3938        .unindent(),
 3939    );
 3940    // Join again
 3941    cx.update_editor(|editor, window, cx| {
 3942        editor.join_lines(&JoinLines, window, cx);
 3943    });
 3944    executor.run_until_parked();
 3945
 3946    cx.assert_editor_state(
 3947        &r#"
 3948        Line 0 Line 1ˇ Line 2
 3949        Line 3
 3950        "#
 3951        .unindent(),
 3952    );
 3953}
 3954
 3955#[gpui::test]
 3956async fn test_custom_newlines_cause_no_false_positive_diffs(
 3957    executor: BackgroundExecutor,
 3958    cx: &mut TestAppContext,
 3959) {
 3960    init_test(cx, |_| {});
 3961    let mut cx = EditorTestContext::new(cx).await;
 3962    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3963    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3964    executor.run_until_parked();
 3965
 3966    cx.update_editor(|editor, window, cx| {
 3967        let snapshot = editor.snapshot(window, cx);
 3968        assert_eq!(
 3969            snapshot
 3970                .buffer_snapshot
 3971                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3972                .collect::<Vec<_>>(),
 3973            Vec::new(),
 3974            "Should not have any diffs for files with custom newlines"
 3975        );
 3976    });
 3977}
 3978
 3979#[gpui::test]
 3980async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3981    init_test(cx, |_| {});
 3982
 3983    let mut cx = EditorTestContext::new(cx).await;
 3984
 3985    // Test sort_lines_case_insensitive()
 3986    cx.set_state(indoc! {"
 3987        «z
 3988        y
 3989        x
 3990        Z
 3991        Y
 3992        Xˇ»
 3993    "});
 3994    cx.update_editor(|e, window, cx| {
 3995        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3996    });
 3997    cx.assert_editor_state(indoc! {"
 3998        «x
 3999        X
 4000        y
 4001        Y
 4002        z
 4003        Zˇ»
 4004    "});
 4005
 4006    // Test reverse_lines()
 4007    cx.set_state(indoc! {"
 4008        «5
 4009        4
 4010        3
 4011        2
 4012        1ˇ»
 4013    "});
 4014    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 4015    cx.assert_editor_state(indoc! {"
 4016        «1
 4017        2
 4018        3
 4019        4
 4020        5ˇ»
 4021    "});
 4022
 4023    // Skip testing shuffle_line()
 4024
 4025    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 4026    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 4027
 4028    // Don't manipulate when cursor is on single line, but expand the selection
 4029    cx.set_state(indoc! {"
 4030        ddˇdd
 4031        ccc
 4032        bb
 4033        a
 4034    "});
 4035    cx.update_editor(|e, window, cx| {
 4036        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4037    });
 4038    cx.assert_editor_state(indoc! {"
 4039        «ddddˇ»
 4040        ccc
 4041        bb
 4042        a
 4043    "});
 4044
 4045    // Basic manipulate case
 4046    // Start selection moves to column 0
 4047    // End of selection shrinks to fit shorter line
 4048    cx.set_state(indoc! {"
 4049        dd«d
 4050        ccc
 4051        bb
 4052        aaaaaˇ»
 4053    "});
 4054    cx.update_editor(|e, window, cx| {
 4055        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4056    });
 4057    cx.assert_editor_state(indoc! {"
 4058        «aaaaa
 4059        bb
 4060        ccc
 4061        dddˇ»
 4062    "});
 4063
 4064    // Manipulate case with newlines
 4065    cx.set_state(indoc! {"
 4066        dd«d
 4067        ccc
 4068
 4069        bb
 4070        aaaaa
 4071
 4072        ˇ»
 4073    "});
 4074    cx.update_editor(|e, window, cx| {
 4075        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4076    });
 4077    cx.assert_editor_state(indoc! {"
 4078        «
 4079
 4080        aaaaa
 4081        bb
 4082        ccc
 4083        dddˇ»
 4084
 4085    "});
 4086
 4087    // Adding new line
 4088    cx.set_state(indoc! {"
 4089        aa«a
 4090        bbˇ»b
 4091    "});
 4092    cx.update_editor(|e, window, cx| {
 4093        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4094    });
 4095    cx.assert_editor_state(indoc! {"
 4096        «aaa
 4097        bbb
 4098        added_lineˇ»
 4099    "});
 4100
 4101    // Removing line
 4102    cx.set_state(indoc! {"
 4103        aa«a
 4104        bbbˇ»
 4105    "});
 4106    cx.update_editor(|e, window, cx| {
 4107        e.manipulate_lines(window, cx, |lines| {
 4108            lines.pop();
 4109        })
 4110    });
 4111    cx.assert_editor_state(indoc! {"
 4112        «aaaˇ»
 4113    "});
 4114
 4115    // Removing all lines
 4116    cx.set_state(indoc! {"
 4117        aa«a
 4118        bbbˇ»
 4119    "});
 4120    cx.update_editor(|e, window, cx| {
 4121        e.manipulate_lines(window, cx, |lines| {
 4122            lines.drain(..);
 4123        })
 4124    });
 4125    cx.assert_editor_state(indoc! {"
 4126        ˇ
 4127    "});
 4128}
 4129
 4130#[gpui::test]
 4131async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4132    init_test(cx, |_| {});
 4133
 4134    let mut cx = EditorTestContext::new(cx).await;
 4135
 4136    // Consider continuous selection as single selection
 4137    cx.set_state(indoc! {"
 4138        Aaa«aa
 4139        cˇ»c«c
 4140        bb
 4141        aaaˇ»aa
 4142    "});
 4143    cx.update_editor(|e, window, cx| {
 4144        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4145    });
 4146    cx.assert_editor_state(indoc! {"
 4147        «Aaaaa
 4148        ccc
 4149        bb
 4150        aaaaaˇ»
 4151    "});
 4152
 4153    cx.set_state(indoc! {"
 4154        Aaa«aa
 4155        cˇ»c«c
 4156        bb
 4157        aaaˇ»aa
 4158    "});
 4159    cx.update_editor(|e, window, cx| {
 4160        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4161    });
 4162    cx.assert_editor_state(indoc! {"
 4163        «Aaaaa
 4164        ccc
 4165        bbˇ»
 4166    "});
 4167
 4168    // Consider non continuous selection as distinct dedup operations
 4169    cx.set_state(indoc! {"
 4170        «aaaaa
 4171        bb
 4172        aaaaa
 4173        aaaaaˇ»
 4174
 4175        aaa«aaˇ»
 4176    "});
 4177    cx.update_editor(|e, window, cx| {
 4178        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4179    });
 4180    cx.assert_editor_state(indoc! {"
 4181        «aaaaa
 4182        bbˇ»
 4183
 4184        «aaaaaˇ»
 4185    "});
 4186}
 4187
 4188#[gpui::test]
 4189async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4190    init_test(cx, |_| {});
 4191
 4192    let mut cx = EditorTestContext::new(cx).await;
 4193
 4194    cx.set_state(indoc! {"
 4195        «Aaa
 4196        aAa
 4197        Aaaˇ»
 4198    "});
 4199    cx.update_editor(|e, window, cx| {
 4200        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4201    });
 4202    cx.assert_editor_state(indoc! {"
 4203        «Aaa
 4204        aAaˇ»
 4205    "});
 4206
 4207    cx.set_state(indoc! {"
 4208        «Aaa
 4209        aAa
 4210        aaAˇ»
 4211    "});
 4212    cx.update_editor(|e, window, cx| {
 4213        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4214    });
 4215    cx.assert_editor_state(indoc! {"
 4216        «Aaaˇ»
 4217    "});
 4218}
 4219
 4220#[gpui::test]
 4221async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4222    init_test(cx, |_| {});
 4223
 4224    let mut cx = EditorTestContext::new(cx).await;
 4225
 4226    // Manipulate with multiple selections on a single line
 4227    cx.set_state(indoc! {"
 4228        dd«dd
 4229        cˇ»c«c
 4230        bb
 4231        aaaˇ»aa
 4232    "});
 4233    cx.update_editor(|e, window, cx| {
 4234        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4235    });
 4236    cx.assert_editor_state(indoc! {"
 4237        «aaaaa
 4238        bb
 4239        ccc
 4240        ddddˇ»
 4241    "});
 4242
 4243    // Manipulate with multiple disjoin selections
 4244    cx.set_state(indoc! {"
 4245 4246        4
 4247        3
 4248        2
 4249        1ˇ»
 4250
 4251        dd«dd
 4252        ccc
 4253        bb
 4254        aaaˇ»aa
 4255    "});
 4256    cx.update_editor(|e, window, cx| {
 4257        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4258    });
 4259    cx.assert_editor_state(indoc! {"
 4260        «1
 4261        2
 4262        3
 4263        4
 4264        5ˇ»
 4265
 4266        «aaaaa
 4267        bb
 4268        ccc
 4269        ddddˇ»
 4270    "});
 4271
 4272    // Adding lines on each selection
 4273    cx.set_state(indoc! {"
 4274 4275        1ˇ»
 4276
 4277        bb«bb
 4278        aaaˇ»aa
 4279    "});
 4280    cx.update_editor(|e, window, cx| {
 4281        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4282    });
 4283    cx.assert_editor_state(indoc! {"
 4284        «2
 4285        1
 4286        added lineˇ»
 4287
 4288        «bbbb
 4289        aaaaa
 4290        added lineˇ»
 4291    "});
 4292
 4293    // Removing lines on each selection
 4294    cx.set_state(indoc! {"
 4295 4296        1ˇ»
 4297
 4298        bb«bb
 4299        aaaˇ»aa
 4300    "});
 4301    cx.update_editor(|e, window, cx| {
 4302        e.manipulate_lines(window, cx, |lines| {
 4303            lines.pop();
 4304        })
 4305    });
 4306    cx.assert_editor_state(indoc! {"
 4307        «2ˇ»
 4308
 4309        «bbbbˇ»
 4310    "});
 4311}
 4312
 4313#[gpui::test]
 4314async fn test_toggle_case(cx: &mut TestAppContext) {
 4315    init_test(cx, |_| {});
 4316
 4317    let mut cx = EditorTestContext::new(cx).await;
 4318
 4319    // If all lower case -> upper case
 4320    cx.set_state(indoc! {"
 4321        «hello worldˇ»
 4322    "});
 4323    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4324    cx.assert_editor_state(indoc! {"
 4325        «HELLO WORLDˇ»
 4326    "});
 4327
 4328    // If all upper case -> lower case
 4329    cx.set_state(indoc! {"
 4330        «HELLO WORLDˇ»
 4331    "});
 4332    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4333    cx.assert_editor_state(indoc! {"
 4334        «hello worldˇ»
 4335    "});
 4336
 4337    // If any upper case characters are identified -> lower case
 4338    // This matches JetBrains IDEs
 4339    cx.set_state(indoc! {"
 4340        «hEllo worldˇ»
 4341    "});
 4342    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4343    cx.assert_editor_state(indoc! {"
 4344        «hello worldˇ»
 4345    "});
 4346}
 4347
 4348#[gpui::test]
 4349async fn test_manipulate_text(cx: &mut TestAppContext) {
 4350    init_test(cx, |_| {});
 4351
 4352    let mut cx = EditorTestContext::new(cx).await;
 4353
 4354    // Test convert_to_upper_case()
 4355    cx.set_state(indoc! {"
 4356        «hello worldˇ»
 4357    "});
 4358    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4359    cx.assert_editor_state(indoc! {"
 4360        «HELLO WORLDˇ»
 4361    "});
 4362
 4363    // Test convert_to_lower_case()
 4364    cx.set_state(indoc! {"
 4365        «HELLO WORLDˇ»
 4366    "});
 4367    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4368    cx.assert_editor_state(indoc! {"
 4369        «hello worldˇ»
 4370    "});
 4371
 4372    // Test multiple line, single selection case
 4373    cx.set_state(indoc! {"
 4374        «The quick brown
 4375        fox jumps over
 4376        the lazy dogˇ»
 4377    "});
 4378    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4379    cx.assert_editor_state(indoc! {"
 4380        «The Quick Brown
 4381        Fox Jumps Over
 4382        The Lazy Dogˇ»
 4383    "});
 4384
 4385    // Test multiple line, single selection case
 4386    cx.set_state(indoc! {"
 4387        «The quick brown
 4388        fox jumps over
 4389        the lazy dogˇ»
 4390    "});
 4391    cx.update_editor(|e, window, cx| {
 4392        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4393    });
 4394    cx.assert_editor_state(indoc! {"
 4395        «TheQuickBrown
 4396        FoxJumpsOver
 4397        TheLazyDogˇ»
 4398    "});
 4399
 4400    // From here on out, test more complex cases of manipulate_text()
 4401
 4402    // Test no selection case - should affect words cursors are in
 4403    // Cursor at beginning, middle, and end of word
 4404    cx.set_state(indoc! {"
 4405        ˇhello big beauˇtiful worldˇ
 4406    "});
 4407    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4408    cx.assert_editor_state(indoc! {"
 4409        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4410    "});
 4411
 4412    // Test multiple selections on a single line and across multiple lines
 4413    cx.set_state(indoc! {"
 4414        «Theˇ» quick «brown
 4415        foxˇ» jumps «overˇ»
 4416        the «lazyˇ» dog
 4417    "});
 4418    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4419    cx.assert_editor_state(indoc! {"
 4420        «THEˇ» quick «BROWN
 4421        FOXˇ» jumps «OVERˇ»
 4422        the «LAZYˇ» dog
 4423    "});
 4424
 4425    // Test case where text length grows
 4426    cx.set_state(indoc! {"
 4427        «tschüߡ»
 4428    "});
 4429    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4430    cx.assert_editor_state(indoc! {"
 4431        «TSCHÜSSˇ»
 4432    "});
 4433
 4434    // Test to make sure we don't crash when text shrinks
 4435    cx.set_state(indoc! {"
 4436        aaa_bbbˇ
 4437    "});
 4438    cx.update_editor(|e, window, cx| {
 4439        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4440    });
 4441    cx.assert_editor_state(indoc! {"
 4442        «aaaBbbˇ»
 4443    "});
 4444
 4445    // Test to make sure we all aware of the fact that each word can grow and shrink
 4446    // Final selections should be aware of this fact
 4447    cx.set_state(indoc! {"
 4448        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4449    "});
 4450    cx.update_editor(|e, window, cx| {
 4451        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4452    });
 4453    cx.assert_editor_state(indoc! {"
 4454        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4455    "});
 4456
 4457    cx.set_state(indoc! {"
 4458        «hElLo, WoRld!ˇ»
 4459    "});
 4460    cx.update_editor(|e, window, cx| {
 4461        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4462    });
 4463    cx.assert_editor_state(indoc! {"
 4464        «HeLlO, wOrLD!ˇ»
 4465    "});
 4466}
 4467
 4468#[gpui::test]
 4469fn test_duplicate_line(cx: &mut TestAppContext) {
 4470    init_test(cx, |_| {});
 4471
 4472    let editor = cx.add_window(|window, cx| {
 4473        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4474        build_editor(buffer, window, cx)
 4475    });
 4476    _ = editor.update(cx, |editor, window, cx| {
 4477        editor.change_selections(None, window, cx, |s| {
 4478            s.select_display_ranges([
 4479                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4480                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4481                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4482                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4483            ])
 4484        });
 4485        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4486        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4487        assert_eq!(
 4488            editor.selections.display_ranges(cx),
 4489            vec![
 4490                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4491                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4492                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4493                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4494            ]
 4495        );
 4496    });
 4497
 4498    let editor = cx.add_window(|window, cx| {
 4499        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4500        build_editor(buffer, window, cx)
 4501    });
 4502    _ = editor.update(cx, |editor, window, cx| {
 4503        editor.change_selections(None, window, cx, |s| {
 4504            s.select_display_ranges([
 4505                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4506                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4507            ])
 4508        });
 4509        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4510        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4511        assert_eq!(
 4512            editor.selections.display_ranges(cx),
 4513            vec![
 4514                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4515                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4516            ]
 4517        );
 4518    });
 4519
 4520    // With `move_upwards` the selections stay in place, except for
 4521    // the lines inserted above them
 4522    let editor = cx.add_window(|window, cx| {
 4523        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4524        build_editor(buffer, window, cx)
 4525    });
 4526    _ = editor.update(cx, |editor, window, cx| {
 4527        editor.change_selections(None, window, cx, |s| {
 4528            s.select_display_ranges([
 4529                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4530                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4531                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4532                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4533            ])
 4534        });
 4535        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4536        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4537        assert_eq!(
 4538            editor.selections.display_ranges(cx),
 4539            vec![
 4540                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4541                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4542                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4543                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4544            ]
 4545        );
 4546    });
 4547
 4548    let editor = cx.add_window(|window, cx| {
 4549        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4550        build_editor(buffer, window, cx)
 4551    });
 4552    _ = editor.update(cx, |editor, window, cx| {
 4553        editor.change_selections(None, window, cx, |s| {
 4554            s.select_display_ranges([
 4555                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4556                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4557            ])
 4558        });
 4559        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4560        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4561        assert_eq!(
 4562            editor.selections.display_ranges(cx),
 4563            vec![
 4564                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4565                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4566            ]
 4567        );
 4568    });
 4569
 4570    let editor = cx.add_window(|window, cx| {
 4571        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4572        build_editor(buffer, window, cx)
 4573    });
 4574    _ = editor.update(cx, |editor, window, cx| {
 4575        editor.change_selections(None, window, cx, |s| {
 4576            s.select_display_ranges([
 4577                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4578                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4579            ])
 4580        });
 4581        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4582        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4583        assert_eq!(
 4584            editor.selections.display_ranges(cx),
 4585            vec![
 4586                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4587                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4588            ]
 4589        );
 4590    });
 4591}
 4592
 4593#[gpui::test]
 4594fn test_move_line_up_down(cx: &mut TestAppContext) {
 4595    init_test(cx, |_| {});
 4596
 4597    let editor = cx.add_window(|window, cx| {
 4598        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4599        build_editor(buffer, window, cx)
 4600    });
 4601    _ = editor.update(cx, |editor, window, cx| {
 4602        editor.fold_creases(
 4603            vec![
 4604                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4605                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4606                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4607            ],
 4608            true,
 4609            window,
 4610            cx,
 4611        );
 4612        editor.change_selections(None, window, cx, |s| {
 4613            s.select_display_ranges([
 4614                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4616                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4617                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4618            ])
 4619        });
 4620        assert_eq!(
 4621            editor.display_text(cx),
 4622            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4623        );
 4624
 4625        editor.move_line_up(&MoveLineUp, window, cx);
 4626        assert_eq!(
 4627            editor.display_text(cx),
 4628            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4629        );
 4630        assert_eq!(
 4631            editor.selections.display_ranges(cx),
 4632            vec![
 4633                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4634                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4635                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4636                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4637            ]
 4638        );
 4639    });
 4640
 4641    _ = editor.update(cx, |editor, window, cx| {
 4642        editor.move_line_down(&MoveLineDown, window, cx);
 4643        assert_eq!(
 4644            editor.display_text(cx),
 4645            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4646        );
 4647        assert_eq!(
 4648            editor.selections.display_ranges(cx),
 4649            vec![
 4650                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4651                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4652                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4653                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4654            ]
 4655        );
 4656    });
 4657
 4658    _ = editor.update(cx, |editor, window, cx| {
 4659        editor.move_line_down(&MoveLineDown, window, cx);
 4660        assert_eq!(
 4661            editor.display_text(cx),
 4662            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4663        );
 4664        assert_eq!(
 4665            editor.selections.display_ranges(cx),
 4666            vec![
 4667                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4668                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4669                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4670                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4671            ]
 4672        );
 4673    });
 4674
 4675    _ = editor.update(cx, |editor, window, cx| {
 4676        editor.move_line_up(&MoveLineUp, window, cx);
 4677        assert_eq!(
 4678            editor.display_text(cx),
 4679            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4680        );
 4681        assert_eq!(
 4682            editor.selections.display_ranges(cx),
 4683            vec![
 4684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4686                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4687                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4688            ]
 4689        );
 4690    });
 4691}
 4692
 4693#[gpui::test]
 4694fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4695    init_test(cx, |_| {});
 4696
 4697    let editor = cx.add_window(|window, cx| {
 4698        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4699        build_editor(buffer, window, cx)
 4700    });
 4701    _ = editor.update(cx, |editor, window, cx| {
 4702        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4703        editor.insert_blocks(
 4704            [BlockProperties {
 4705                style: BlockStyle::Fixed,
 4706                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4707                height: Some(1),
 4708                render: Arc::new(|_| div().into_any()),
 4709                priority: 0,
 4710                render_in_minimap: true,
 4711            }],
 4712            Some(Autoscroll::fit()),
 4713            cx,
 4714        );
 4715        editor.change_selections(None, window, cx, |s| {
 4716            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4717        });
 4718        editor.move_line_down(&MoveLineDown, window, cx);
 4719    });
 4720}
 4721
 4722#[gpui::test]
 4723async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4724    init_test(cx, |_| {});
 4725
 4726    let mut cx = EditorTestContext::new(cx).await;
 4727    cx.set_state(
 4728        &"
 4729            ˇzero
 4730            one
 4731            two
 4732            three
 4733            four
 4734            five
 4735        "
 4736        .unindent(),
 4737    );
 4738
 4739    // Create a four-line block that replaces three lines of text.
 4740    cx.update_editor(|editor, window, cx| {
 4741        let snapshot = editor.snapshot(window, cx);
 4742        let snapshot = &snapshot.buffer_snapshot;
 4743        let placement = BlockPlacement::Replace(
 4744            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4745        );
 4746        editor.insert_blocks(
 4747            [BlockProperties {
 4748                placement,
 4749                height: Some(4),
 4750                style: BlockStyle::Sticky,
 4751                render: Arc::new(|_| gpui::div().into_any_element()),
 4752                priority: 0,
 4753                render_in_minimap: true,
 4754            }],
 4755            None,
 4756            cx,
 4757        );
 4758    });
 4759
 4760    // Move down so that the cursor touches the block.
 4761    cx.update_editor(|editor, window, cx| {
 4762        editor.move_down(&Default::default(), window, cx);
 4763    });
 4764    cx.assert_editor_state(
 4765        &"
 4766            zero
 4767            «one
 4768            two
 4769            threeˇ»
 4770            four
 4771            five
 4772        "
 4773        .unindent(),
 4774    );
 4775
 4776    // Move down past the block.
 4777    cx.update_editor(|editor, window, cx| {
 4778        editor.move_down(&Default::default(), window, cx);
 4779    });
 4780    cx.assert_editor_state(
 4781        &"
 4782            zero
 4783            one
 4784            two
 4785            three
 4786            ˇfour
 4787            five
 4788        "
 4789        .unindent(),
 4790    );
 4791}
 4792
 4793#[gpui::test]
 4794fn test_transpose(cx: &mut TestAppContext) {
 4795    init_test(cx, |_| {});
 4796
 4797    _ = cx.add_window(|window, cx| {
 4798        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4799        editor.set_style(EditorStyle::default(), window, cx);
 4800        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4801        editor.transpose(&Default::default(), window, cx);
 4802        assert_eq!(editor.text(cx), "bac");
 4803        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4804
 4805        editor.transpose(&Default::default(), window, cx);
 4806        assert_eq!(editor.text(cx), "bca");
 4807        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4808
 4809        editor.transpose(&Default::default(), window, cx);
 4810        assert_eq!(editor.text(cx), "bac");
 4811        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4812
 4813        editor
 4814    });
 4815
 4816    _ = cx.add_window(|window, cx| {
 4817        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4818        editor.set_style(EditorStyle::default(), window, cx);
 4819        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4820        editor.transpose(&Default::default(), window, cx);
 4821        assert_eq!(editor.text(cx), "acb\nde");
 4822        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4823
 4824        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4825        editor.transpose(&Default::default(), window, cx);
 4826        assert_eq!(editor.text(cx), "acbd\ne");
 4827        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4828
 4829        editor.transpose(&Default::default(), window, cx);
 4830        assert_eq!(editor.text(cx), "acbde\n");
 4831        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4832
 4833        editor.transpose(&Default::default(), window, cx);
 4834        assert_eq!(editor.text(cx), "acbd\ne");
 4835        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4836
 4837        editor
 4838    });
 4839
 4840    _ = cx.add_window(|window, cx| {
 4841        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4842        editor.set_style(EditorStyle::default(), window, cx);
 4843        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4844        editor.transpose(&Default::default(), window, cx);
 4845        assert_eq!(editor.text(cx), "bacd\ne");
 4846        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4847
 4848        editor.transpose(&Default::default(), window, cx);
 4849        assert_eq!(editor.text(cx), "bcade\n");
 4850        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4851
 4852        editor.transpose(&Default::default(), window, cx);
 4853        assert_eq!(editor.text(cx), "bcda\ne");
 4854        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4855
 4856        editor.transpose(&Default::default(), window, cx);
 4857        assert_eq!(editor.text(cx), "bcade\n");
 4858        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4859
 4860        editor.transpose(&Default::default(), window, cx);
 4861        assert_eq!(editor.text(cx), "bcaed\n");
 4862        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4863
 4864        editor
 4865    });
 4866
 4867    _ = cx.add_window(|window, cx| {
 4868        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4869        editor.set_style(EditorStyle::default(), window, cx);
 4870        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4871        editor.transpose(&Default::default(), window, cx);
 4872        assert_eq!(editor.text(cx), "🏀🍐✋");
 4873        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4874
 4875        editor.transpose(&Default::default(), window, cx);
 4876        assert_eq!(editor.text(cx), "🏀✋🍐");
 4877        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4878
 4879        editor.transpose(&Default::default(), window, cx);
 4880        assert_eq!(editor.text(cx), "🏀🍐✋");
 4881        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4882
 4883        editor
 4884    });
 4885}
 4886
 4887#[gpui::test]
 4888async fn test_rewrap(cx: &mut TestAppContext) {
 4889    init_test(cx, |settings| {
 4890        settings.languages.extend([
 4891            (
 4892                "Markdown".into(),
 4893                LanguageSettingsContent {
 4894                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4895                    ..Default::default()
 4896                },
 4897            ),
 4898            (
 4899                "Plain Text".into(),
 4900                LanguageSettingsContent {
 4901                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4902                    ..Default::default()
 4903                },
 4904            ),
 4905        ])
 4906    });
 4907
 4908    let mut cx = EditorTestContext::new(cx).await;
 4909
 4910    let language_with_c_comments = Arc::new(Language::new(
 4911        LanguageConfig {
 4912            line_comments: vec!["// ".into()],
 4913            ..LanguageConfig::default()
 4914        },
 4915        None,
 4916    ));
 4917    let language_with_pound_comments = Arc::new(Language::new(
 4918        LanguageConfig {
 4919            line_comments: vec!["# ".into()],
 4920            ..LanguageConfig::default()
 4921        },
 4922        None,
 4923    ));
 4924    let markdown_language = Arc::new(Language::new(
 4925        LanguageConfig {
 4926            name: "Markdown".into(),
 4927            ..LanguageConfig::default()
 4928        },
 4929        None,
 4930    ));
 4931    let language_with_doc_comments = Arc::new(Language::new(
 4932        LanguageConfig {
 4933            line_comments: vec!["// ".into(), "/// ".into()],
 4934            ..LanguageConfig::default()
 4935        },
 4936        Some(tree_sitter_rust::LANGUAGE.into()),
 4937    ));
 4938
 4939    let plaintext_language = Arc::new(Language::new(
 4940        LanguageConfig {
 4941            name: "Plain Text".into(),
 4942            ..LanguageConfig::default()
 4943        },
 4944        None,
 4945    ));
 4946
 4947    assert_rewrap(
 4948        indoc! {"
 4949            // ˇ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.
 4950        "},
 4951        indoc! {"
 4952            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4953            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4954            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4955            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4956            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4957            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4958            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4959            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4960            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4961            // porttitor id. Aliquam id accumsan eros.
 4962        "},
 4963        language_with_c_comments.clone(),
 4964        &mut cx,
 4965    );
 4966
 4967    // Test that rewrapping works inside of a selection
 4968    assert_rewrap(
 4969        indoc! {"
 4970            «// 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.ˇ»
 4971        "},
 4972        indoc! {"
 4973            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4974            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4975            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4976            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4977            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4978            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4979            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4980            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4981            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4982            // porttitor id. Aliquam id accumsan eros.ˇ»
 4983        "},
 4984        language_with_c_comments.clone(),
 4985        &mut cx,
 4986    );
 4987
 4988    // Test that cursors that expand to the same region are collapsed.
 4989    assert_rewrap(
 4990        indoc! {"
 4991            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4992            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4993            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4994            // ˇ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.
 4995        "},
 4996        indoc! {"
 4997            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4998            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4999            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 5000            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 5001            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 5002            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 5003            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 5004            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 5005            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 5006            // porttitor id. Aliquam id accumsan eros.
 5007        "},
 5008        language_with_c_comments.clone(),
 5009        &mut cx,
 5010    );
 5011
 5012    // Test that non-contiguous selections are treated separately.
 5013    assert_rewrap(
 5014        indoc! {"
 5015            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 5016            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 5017            //
 5018            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5019            // ˇ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.
 5020        "},
 5021        indoc! {"
 5022            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 5023            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 5024            // auctor, eu lacinia sapien scelerisque.
 5025            //
 5026            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 5027            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5028            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 5029            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 5030            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 5031            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 5032            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 5033        "},
 5034        language_with_c_comments.clone(),
 5035        &mut cx,
 5036    );
 5037
 5038    // Test that different comment prefixes are supported.
 5039    assert_rewrap(
 5040        indoc! {"
 5041            # ˇ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.
 5042        "},
 5043        indoc! {"
 5044            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 5045            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5046            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5047            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5048            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 5049            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 5050            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 5051            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 5052            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 5053            # accumsan eros.
 5054        "},
 5055        language_with_pound_comments.clone(),
 5056        &mut cx,
 5057    );
 5058
 5059    // Test that rewrapping is ignored outside of comments in most languages.
 5060    assert_rewrap(
 5061        indoc! {"
 5062            /// Adds two numbers.
 5063            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5064            fn add(a: u32, b: u32) -> u32 {
 5065                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ˇ
 5066            }
 5067        "},
 5068        indoc! {"
 5069            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 5070            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5071            fn add(a: u32, b: u32) -> u32 {
 5072                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ˇ
 5073            }
 5074        "},
 5075        language_with_doc_comments.clone(),
 5076        &mut cx,
 5077    );
 5078
 5079    // Test that rewrapping works in Markdown and Plain Text languages.
 5080    assert_rewrap(
 5081        indoc! {"
 5082            # Hello
 5083
 5084            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.
 5085        "},
 5086        indoc! {"
 5087            # Hello
 5088
 5089            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5090            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5091            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5092            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5093            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5094            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5095            Integer sit amet scelerisque nisi.
 5096        "},
 5097        markdown_language,
 5098        &mut cx,
 5099    );
 5100
 5101    assert_rewrap(
 5102        indoc! {"
 5103            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.
 5104        "},
 5105        indoc! {"
 5106            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5107            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5108            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5109            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5110            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5111            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5112            Integer sit amet scelerisque nisi.
 5113        "},
 5114        plaintext_language.clone(),
 5115        &mut cx,
 5116    );
 5117
 5118    // Test rewrapping unaligned comments in a selection.
 5119    assert_rewrap(
 5120        indoc! {"
 5121            fn foo() {
 5122                if true {
 5123            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5124            // Praesent semper egestas tellus id dignissim.ˇ»
 5125                    do_something();
 5126                } else {
 5127                    //
 5128                }
 5129            }
 5130        "},
 5131        indoc! {"
 5132            fn foo() {
 5133                if true {
 5134            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5135                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5136                    // egestas tellus id dignissim.ˇ»
 5137                    do_something();
 5138                } else {
 5139                    //
 5140                }
 5141            }
 5142        "},
 5143        language_with_doc_comments.clone(),
 5144        &mut cx,
 5145    );
 5146
 5147    assert_rewrap(
 5148        indoc! {"
 5149            fn foo() {
 5150                if true {
 5151            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5152            // Praesent semper egestas tellus id dignissim.»
 5153                    do_something();
 5154                } else {
 5155                    //
 5156                }
 5157
 5158            }
 5159        "},
 5160        indoc! {"
 5161            fn foo() {
 5162                if true {
 5163            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5164                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5165                    // egestas tellus id dignissim.»
 5166                    do_something();
 5167                } else {
 5168                    //
 5169                }
 5170
 5171            }
 5172        "},
 5173        language_with_doc_comments.clone(),
 5174        &mut cx,
 5175    );
 5176
 5177    assert_rewrap(
 5178        indoc! {"
 5179            «ˇone one one one one one one one one one one one one one one one one one one one one one one one one
 5180
 5181            two»
 5182
 5183            three
 5184
 5185            «ˇ\t
 5186
 5187            four four four four four four four four four four four four four four four four four four four four»
 5188
 5189            «ˇfive five five five five five five five five five five five five five five five five five five five
 5190            \t»
 5191            six six six six six six six six six six six six six six six six six six six six six six six six six
 5192        "},
 5193        indoc! {"
 5194            «ˇone one one one one one one one one one one one one one one one one one one one
 5195            one one one one one
 5196
 5197            two»
 5198
 5199            three
 5200
 5201            «ˇ\t
 5202
 5203            four four four four four four four four four four four four four four four four
 5204            four four four four»
 5205
 5206            «ˇfive five five five five five five five five five five five five five five five
 5207            five five five five
 5208            \t»
 5209            six six six six six six six six six six six six six six six six six six six six six six six six six
 5210        "},
 5211        plaintext_language.clone(),
 5212        &mut cx,
 5213    );
 5214
 5215    assert_rewrap(
 5216        indoc! {"
 5217            //ˇ long long long long long long long long long long long long long long long long long long long long long long long long long long long long
 5218            //ˇ
 5219            //ˇ long long long long long long long long long long long long long long long long long long long long long long long long long long long long
 5220            //ˇ short short short
 5221            int main(void) {
 5222                return 17;
 5223            }
 5224        "},
 5225        indoc! {"
 5226            //ˇ long long long long long long long long long long long long long long long
 5227            // long long long long long long long long long long long long long
 5228            //ˇ
 5229            //ˇ long long long long long long long long long long long long long long long
 5230            //ˇ long long long long long long long long long long long long long short short
 5231            // short
 5232            int main(void) {
 5233                return 17;
 5234            }
 5235        "},
 5236        language_with_c_comments,
 5237        &mut cx,
 5238    );
 5239
 5240    #[track_caller]
 5241    fn assert_rewrap(
 5242        unwrapped_text: &str,
 5243        wrapped_text: &str,
 5244        language: Arc<Language>,
 5245        cx: &mut EditorTestContext,
 5246    ) {
 5247        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5248        cx.set_state(unwrapped_text);
 5249        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5250        cx.assert_editor_state(wrapped_text);
 5251    }
 5252}
 5253
 5254#[gpui::test]
 5255async fn test_hard_wrap(cx: &mut TestAppContext) {
 5256    init_test(cx, |_| {});
 5257    let mut cx = EditorTestContext::new(cx).await;
 5258
 5259    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5260    cx.update_editor(|editor, _, cx| {
 5261        editor.set_hard_wrap(Some(14), cx);
 5262    });
 5263
 5264    cx.set_state(indoc!(
 5265        "
 5266        one two three ˇ
 5267        "
 5268    ));
 5269    cx.simulate_input("four");
 5270    cx.run_until_parked();
 5271
 5272    cx.assert_editor_state(indoc!(
 5273        "
 5274        one two three
 5275        fourˇ
 5276        "
 5277    ));
 5278
 5279    cx.update_editor(|editor, window, cx| {
 5280        editor.newline(&Default::default(), window, cx);
 5281    });
 5282    cx.run_until_parked();
 5283    cx.assert_editor_state(indoc!(
 5284        "
 5285        one two three
 5286        four
 5287        ˇ
 5288        "
 5289    ));
 5290
 5291    cx.simulate_input("five");
 5292    cx.run_until_parked();
 5293    cx.assert_editor_state(indoc!(
 5294        "
 5295        one two three
 5296        four
 5297        fiveˇ
 5298        "
 5299    ));
 5300
 5301    cx.update_editor(|editor, window, cx| {
 5302        editor.newline(&Default::default(), window, cx);
 5303    });
 5304    cx.run_until_parked();
 5305    cx.simulate_input("# ");
 5306    cx.run_until_parked();
 5307    cx.assert_editor_state(indoc!(
 5308        "
 5309        one two three
 5310        four
 5311        five
 5312        # ˇ
 5313        "
 5314    ));
 5315
 5316    cx.update_editor(|editor, window, cx| {
 5317        editor.newline(&Default::default(), window, cx);
 5318    });
 5319    cx.run_until_parked();
 5320    cx.assert_editor_state(indoc!(
 5321        "
 5322        one two three
 5323        four
 5324        five
 5325        #\x20
 5326 5327        "
 5328    ));
 5329
 5330    cx.simulate_input(" 6");
 5331    cx.run_until_parked();
 5332    cx.assert_editor_state(indoc!(
 5333        "
 5334        one two three
 5335        four
 5336        five
 5337        #
 5338        # 6ˇ
 5339        "
 5340    ));
 5341}
 5342
 5343#[gpui::test]
 5344async fn test_clipboard(cx: &mut TestAppContext) {
 5345    init_test(cx, |_| {});
 5346
 5347    let mut cx = EditorTestContext::new(cx).await;
 5348
 5349    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5350    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5351    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5352
 5353    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5354    cx.set_state("two ˇfour ˇsix ˇ");
 5355    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5356    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5357
 5358    // Paste again but with only two cursors. Since the number of cursors doesn't
 5359    // match the number of slices in the clipboard, the entire clipboard text
 5360    // is pasted at each cursor.
 5361    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5362    cx.update_editor(|e, window, cx| {
 5363        e.handle_input("( ", window, cx);
 5364        e.paste(&Paste, window, cx);
 5365        e.handle_input(") ", window, cx);
 5366    });
 5367    cx.assert_editor_state(
 5368        &([
 5369            "( one✅ ",
 5370            "three ",
 5371            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5372            "three ",
 5373            "five ) ˇ",
 5374        ]
 5375        .join("\n")),
 5376    );
 5377
 5378    // Cut with three selections, one of which is full-line.
 5379    cx.set_state(indoc! {"
 5380        1«2ˇ»3
 5381        4ˇ567
 5382        «8ˇ»9"});
 5383    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5384    cx.assert_editor_state(indoc! {"
 5385        1ˇ3
 5386        ˇ9"});
 5387
 5388    // Paste with three selections, noticing how the copied selection that was full-line
 5389    // gets inserted before the second cursor.
 5390    cx.set_state(indoc! {"
 5391        1ˇ3
 5392 5393        «oˇ»ne"});
 5394    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5395    cx.assert_editor_state(indoc! {"
 5396        12ˇ3
 5397        4567
 5398 5399        8ˇne"});
 5400
 5401    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5402    cx.set_state(indoc! {"
 5403        The quick brown
 5404        fox juˇmps over
 5405        the lazy dog"});
 5406    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5407    assert_eq!(
 5408        cx.read_from_clipboard()
 5409            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5410        Some("fox jumps over\n".to_string())
 5411    );
 5412
 5413    // Paste with three selections, noticing how the copied full-line selection is inserted
 5414    // before the empty selections but replaces the selection that is non-empty.
 5415    cx.set_state(indoc! {"
 5416        Tˇhe quick brown
 5417        «foˇ»x jumps over
 5418        tˇhe lazy dog"});
 5419    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5420    cx.assert_editor_state(indoc! {"
 5421        fox jumps over
 5422        Tˇhe quick brown
 5423        fox jumps over
 5424        ˇx jumps over
 5425        fox jumps over
 5426        tˇhe lazy dog"});
 5427}
 5428
 5429#[gpui::test]
 5430async fn test_copy_trim(cx: &mut TestAppContext) {
 5431    init_test(cx, |_| {});
 5432
 5433    let mut cx = EditorTestContext::new(cx).await;
 5434    cx.set_state(
 5435        r#"            «for selection in selections.iter() {
 5436            let mut start = selection.start;
 5437            let mut end = selection.end;
 5438            let is_entire_line = selection.is_empty();
 5439            if is_entire_line {
 5440                start = Point::new(start.row, 0);ˇ»
 5441                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5442            }
 5443        "#,
 5444    );
 5445    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5446    assert_eq!(
 5447        cx.read_from_clipboard()
 5448            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5449        Some(
 5450            "for selection in selections.iter() {
 5451            let mut start = selection.start;
 5452            let mut end = selection.end;
 5453            let is_entire_line = selection.is_empty();
 5454            if is_entire_line {
 5455                start = Point::new(start.row, 0);"
 5456                .to_string()
 5457        ),
 5458        "Regular copying preserves all indentation selected",
 5459    );
 5460    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5461    assert_eq!(
 5462        cx.read_from_clipboard()
 5463            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5464        Some(
 5465            "for selection in selections.iter() {
 5466let mut start = selection.start;
 5467let mut end = selection.end;
 5468let is_entire_line = selection.is_empty();
 5469if is_entire_line {
 5470    start = Point::new(start.row, 0);"
 5471                .to_string()
 5472        ),
 5473        "Copying with stripping should strip all leading whitespaces"
 5474    );
 5475
 5476    cx.set_state(
 5477        r#"       «     for selection in selections.iter() {
 5478            let mut start = selection.start;
 5479            let mut end = selection.end;
 5480            let is_entire_line = selection.is_empty();
 5481            if is_entire_line {
 5482                start = Point::new(start.row, 0);ˇ»
 5483                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5484            }
 5485        "#,
 5486    );
 5487    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5488    assert_eq!(
 5489        cx.read_from_clipboard()
 5490            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5491        Some(
 5492            "     for selection in selections.iter() {
 5493            let mut start = selection.start;
 5494            let mut end = selection.end;
 5495            let is_entire_line = selection.is_empty();
 5496            if is_entire_line {
 5497                start = Point::new(start.row, 0);"
 5498                .to_string()
 5499        ),
 5500        "Regular copying preserves all indentation selected",
 5501    );
 5502    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5503    assert_eq!(
 5504        cx.read_from_clipboard()
 5505            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5506        Some(
 5507            "for selection in selections.iter() {
 5508let mut start = selection.start;
 5509let mut end = selection.end;
 5510let is_entire_line = selection.is_empty();
 5511if is_entire_line {
 5512    start = Point::new(start.row, 0);"
 5513                .to_string()
 5514        ),
 5515        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5516    );
 5517
 5518    cx.set_state(
 5519        r#"       «ˇ     for selection in selections.iter() {
 5520            let mut start = selection.start;
 5521            let mut end = selection.end;
 5522            let is_entire_line = selection.is_empty();
 5523            if is_entire_line {
 5524                start = Point::new(start.row, 0);»
 5525                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5526            }
 5527        "#,
 5528    );
 5529    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5530    assert_eq!(
 5531        cx.read_from_clipboard()
 5532            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5533        Some(
 5534            "     for selection in selections.iter() {
 5535            let mut start = selection.start;
 5536            let mut end = selection.end;
 5537            let is_entire_line = selection.is_empty();
 5538            if is_entire_line {
 5539                start = Point::new(start.row, 0);"
 5540                .to_string()
 5541        ),
 5542        "Regular copying for reverse selection works the same",
 5543    );
 5544    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5545    assert_eq!(
 5546        cx.read_from_clipboard()
 5547            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5548        Some(
 5549            "for selection in selections.iter() {
 5550let mut start = selection.start;
 5551let mut end = selection.end;
 5552let is_entire_line = selection.is_empty();
 5553if is_entire_line {
 5554    start = Point::new(start.row, 0);"
 5555                .to_string()
 5556        ),
 5557        "Copying with stripping for reverse selection works the same"
 5558    );
 5559
 5560    cx.set_state(
 5561        r#"            for selection «in selections.iter() {
 5562            let mut start = selection.start;
 5563            let mut end = selection.end;
 5564            let is_entire_line = selection.is_empty();
 5565            if is_entire_line {
 5566                start = Point::new(start.row, 0);ˇ»
 5567                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5568            }
 5569        "#,
 5570    );
 5571    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5572    assert_eq!(
 5573        cx.read_from_clipboard()
 5574            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5575        Some(
 5576            "in selections.iter() {
 5577            let mut start = selection.start;
 5578            let mut end = selection.end;
 5579            let is_entire_line = selection.is_empty();
 5580            if is_entire_line {
 5581                start = Point::new(start.row, 0);"
 5582                .to_string()
 5583        ),
 5584        "When selecting past the indent, the copying works as usual",
 5585    );
 5586    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5587    assert_eq!(
 5588        cx.read_from_clipboard()
 5589            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5590        Some(
 5591            "in selections.iter() {
 5592            let mut start = selection.start;
 5593            let mut end = selection.end;
 5594            let is_entire_line = selection.is_empty();
 5595            if is_entire_line {
 5596                start = Point::new(start.row, 0);"
 5597                .to_string()
 5598        ),
 5599        "When selecting past the indent, nothing is trimmed"
 5600    );
 5601
 5602    cx.set_state(
 5603        r#"            «for selection in selections.iter() {
 5604            let mut start = selection.start;
 5605
 5606            let mut end = selection.end;
 5607            let is_entire_line = selection.is_empty();
 5608            if is_entire_line {
 5609                start = Point::new(start.row, 0);
 5610ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5611            }
 5612        "#,
 5613    );
 5614    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5615    assert_eq!(
 5616        cx.read_from_clipboard()
 5617            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5618        Some(
 5619            "for selection in selections.iter() {
 5620let mut start = selection.start;
 5621
 5622let mut end = selection.end;
 5623let is_entire_line = selection.is_empty();
 5624if is_entire_line {
 5625    start = Point::new(start.row, 0);
 5626"
 5627            .to_string()
 5628        ),
 5629        "Copying with stripping should ignore empty lines"
 5630    );
 5631}
 5632
 5633#[gpui::test]
 5634async fn test_paste_multiline(cx: &mut TestAppContext) {
 5635    init_test(cx, |_| {});
 5636
 5637    let mut cx = EditorTestContext::new(cx).await;
 5638    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5639
 5640    // Cut an indented block, without the leading whitespace.
 5641    cx.set_state(indoc! {"
 5642        const a: B = (
 5643            c(),
 5644            «d(
 5645                e,
 5646                f
 5647            )ˇ»
 5648        );
 5649    "});
 5650    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5651    cx.assert_editor_state(indoc! {"
 5652        const a: B = (
 5653            c(),
 5654            ˇ
 5655        );
 5656    "});
 5657
 5658    // Paste it at the same position.
 5659    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5660    cx.assert_editor_state(indoc! {"
 5661        const a: B = (
 5662            c(),
 5663            d(
 5664                e,
 5665                f
 5666 5667        );
 5668    "});
 5669
 5670    // Paste it at a line with a lower indent level.
 5671    cx.set_state(indoc! {"
 5672        ˇ
 5673        const a: B = (
 5674            c(),
 5675        );
 5676    "});
 5677    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5678    cx.assert_editor_state(indoc! {"
 5679        d(
 5680            e,
 5681            f
 5682 5683        const a: B = (
 5684            c(),
 5685        );
 5686    "});
 5687
 5688    // Cut an indented block, with the leading whitespace.
 5689    cx.set_state(indoc! {"
 5690        const a: B = (
 5691            c(),
 5692        «    d(
 5693                e,
 5694                f
 5695            )
 5696        ˇ»);
 5697    "});
 5698    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5699    cx.assert_editor_state(indoc! {"
 5700        const a: B = (
 5701            c(),
 5702        ˇ);
 5703    "});
 5704
 5705    // Paste it at the same position.
 5706    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5707    cx.assert_editor_state(indoc! {"
 5708        const a: B = (
 5709            c(),
 5710            d(
 5711                e,
 5712                f
 5713            )
 5714        ˇ);
 5715    "});
 5716
 5717    // Paste it at a line with a higher indent level.
 5718    cx.set_state(indoc! {"
 5719        const a: B = (
 5720            c(),
 5721            d(
 5722                e,
 5723 5724            )
 5725        );
 5726    "});
 5727    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5728    cx.assert_editor_state(indoc! {"
 5729        const a: B = (
 5730            c(),
 5731            d(
 5732                e,
 5733                f    d(
 5734                    e,
 5735                    f
 5736                )
 5737        ˇ
 5738            )
 5739        );
 5740    "});
 5741
 5742    // Copy an indented block, starting mid-line
 5743    cx.set_state(indoc! {"
 5744        const a: B = (
 5745            c(),
 5746            somethin«g(
 5747                e,
 5748                f
 5749            )ˇ»
 5750        );
 5751    "});
 5752    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5753
 5754    // Paste it on a line with a lower indent level
 5755    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5756    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5757    cx.assert_editor_state(indoc! {"
 5758        const a: B = (
 5759            c(),
 5760            something(
 5761                e,
 5762                f
 5763            )
 5764        );
 5765        g(
 5766            e,
 5767            f
 5768"});
 5769}
 5770
 5771#[gpui::test]
 5772async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5773    init_test(cx, |_| {});
 5774
 5775    cx.write_to_clipboard(ClipboardItem::new_string(
 5776        "    d(\n        e\n    );\n".into(),
 5777    ));
 5778
 5779    let mut cx = EditorTestContext::new(cx).await;
 5780    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5781
 5782    cx.set_state(indoc! {"
 5783        fn a() {
 5784            b();
 5785            if c() {
 5786                ˇ
 5787            }
 5788        }
 5789    "});
 5790
 5791    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5792    cx.assert_editor_state(indoc! {"
 5793        fn a() {
 5794            b();
 5795            if c() {
 5796                d(
 5797                    e
 5798                );
 5799        ˇ
 5800            }
 5801        }
 5802    "});
 5803
 5804    cx.set_state(indoc! {"
 5805        fn a() {
 5806            b();
 5807            ˇ
 5808        }
 5809    "});
 5810
 5811    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5812    cx.assert_editor_state(indoc! {"
 5813        fn a() {
 5814            b();
 5815            d(
 5816                e
 5817            );
 5818        ˇ
 5819        }
 5820    "});
 5821}
 5822
 5823#[gpui::test]
 5824fn test_select_all(cx: &mut TestAppContext) {
 5825    init_test(cx, |_| {});
 5826
 5827    let editor = cx.add_window(|window, cx| {
 5828        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5829        build_editor(buffer, window, cx)
 5830    });
 5831    _ = editor.update(cx, |editor, window, cx| {
 5832        editor.select_all(&SelectAll, window, cx);
 5833        assert_eq!(
 5834            editor.selections.display_ranges(cx),
 5835            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5836        );
 5837    });
 5838}
 5839
 5840#[gpui::test]
 5841fn test_select_line(cx: &mut TestAppContext) {
 5842    init_test(cx, |_| {});
 5843
 5844    let editor = cx.add_window(|window, cx| {
 5845        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5846        build_editor(buffer, window, cx)
 5847    });
 5848    _ = editor.update(cx, |editor, window, cx| {
 5849        editor.change_selections(None, window, cx, |s| {
 5850            s.select_display_ranges([
 5851                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5852                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5853                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5854                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5855            ])
 5856        });
 5857        editor.select_line(&SelectLine, window, cx);
 5858        assert_eq!(
 5859            editor.selections.display_ranges(cx),
 5860            vec![
 5861                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5862                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5863            ]
 5864        );
 5865    });
 5866
 5867    _ = editor.update(cx, |editor, window, cx| {
 5868        editor.select_line(&SelectLine, window, cx);
 5869        assert_eq!(
 5870            editor.selections.display_ranges(cx),
 5871            vec![
 5872                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5873                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5874            ]
 5875        );
 5876    });
 5877
 5878    _ = editor.update(cx, |editor, window, cx| {
 5879        editor.select_line(&SelectLine, window, cx);
 5880        assert_eq!(
 5881            editor.selections.display_ranges(cx),
 5882            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5883        );
 5884    });
 5885}
 5886
 5887#[gpui::test]
 5888async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5889    init_test(cx, |_| {});
 5890    let mut cx = EditorTestContext::new(cx).await;
 5891
 5892    #[track_caller]
 5893    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5894        cx.set_state(initial_state);
 5895        cx.update_editor(|e, window, cx| {
 5896            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5897        });
 5898        cx.assert_editor_state(expected_state);
 5899    }
 5900
 5901    // Selection starts and ends at the middle of lines, left-to-right
 5902    test(
 5903        &mut cx,
 5904        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5905        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5906    );
 5907    // Same thing, right-to-left
 5908    test(
 5909        &mut cx,
 5910        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5911        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5912    );
 5913
 5914    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5915    test(
 5916        &mut cx,
 5917        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5918        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5919    );
 5920    // Same thing, right-to-left
 5921    test(
 5922        &mut cx,
 5923        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5924        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5925    );
 5926
 5927    // Whole buffer, left-to-right, last line ends with newline
 5928    test(
 5929        &mut cx,
 5930        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5931        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5932    );
 5933    // Same thing, right-to-left
 5934    test(
 5935        &mut cx,
 5936        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5937        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5938    );
 5939
 5940    // Starts at the end of a line, ends at the start of another
 5941    test(
 5942        &mut cx,
 5943        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5944        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5945    );
 5946}
 5947
 5948#[gpui::test]
 5949async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5950    init_test(cx, |_| {});
 5951
 5952    let editor = cx.add_window(|window, cx| {
 5953        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5954        build_editor(buffer, window, cx)
 5955    });
 5956
 5957    // setup
 5958    _ = editor.update(cx, |editor, window, cx| {
 5959        editor.fold_creases(
 5960            vec![
 5961                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5962                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5963                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5964            ],
 5965            true,
 5966            window,
 5967            cx,
 5968        );
 5969        assert_eq!(
 5970            editor.display_text(cx),
 5971            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5972        );
 5973    });
 5974
 5975    _ = editor.update(cx, |editor, window, cx| {
 5976        editor.change_selections(None, window, cx, |s| {
 5977            s.select_display_ranges([
 5978                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5979                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5980                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5981                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5982            ])
 5983        });
 5984        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5985        assert_eq!(
 5986            editor.display_text(cx),
 5987            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5988        );
 5989    });
 5990    EditorTestContext::for_editor(editor, cx)
 5991        .await
 5992        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5993
 5994    _ = editor.update(cx, |editor, window, cx| {
 5995        editor.change_selections(None, window, cx, |s| {
 5996            s.select_display_ranges([
 5997                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5998            ])
 5999        });
 6000        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 6001        assert_eq!(
 6002            editor.display_text(cx),
 6003            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 6004        );
 6005        assert_eq!(
 6006            editor.selections.display_ranges(cx),
 6007            [
 6008                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 6009                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 6010                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 6011                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 6012                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 6013                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 6014                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 6015            ]
 6016        );
 6017    });
 6018    EditorTestContext::for_editor(editor, cx)
 6019        .await
 6020        .assert_editor_state(
 6021            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 6022        );
 6023}
 6024
 6025#[gpui::test]
 6026async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 6027    init_test(cx, |_| {});
 6028
 6029    let mut cx = EditorTestContext::new(cx).await;
 6030
 6031    cx.set_state(indoc!(
 6032        r#"abc
 6033           defˇghi
 6034
 6035           jk
 6036           nlmo
 6037           "#
 6038    ));
 6039
 6040    cx.update_editor(|editor, window, cx| {
 6041        editor.add_selection_above(&Default::default(), window, cx);
 6042    });
 6043
 6044    cx.assert_editor_state(indoc!(
 6045        r#"abcˇ
 6046           defˇghi
 6047
 6048           jk
 6049           nlmo
 6050           "#
 6051    ));
 6052
 6053    cx.update_editor(|editor, window, cx| {
 6054        editor.add_selection_above(&Default::default(), window, cx);
 6055    });
 6056
 6057    cx.assert_editor_state(indoc!(
 6058        r#"abcˇ
 6059            defˇghi
 6060
 6061            jk
 6062            nlmo
 6063            "#
 6064    ));
 6065
 6066    cx.update_editor(|editor, window, cx| {
 6067        editor.add_selection_below(&Default::default(), window, cx);
 6068    });
 6069
 6070    cx.assert_editor_state(indoc!(
 6071        r#"abc
 6072           defˇghi
 6073
 6074           jk
 6075           nlmo
 6076           "#
 6077    ));
 6078
 6079    cx.update_editor(|editor, window, cx| {
 6080        editor.undo_selection(&Default::default(), window, cx);
 6081    });
 6082
 6083    cx.assert_editor_state(indoc!(
 6084        r#"abcˇ
 6085           defˇghi
 6086
 6087           jk
 6088           nlmo
 6089           "#
 6090    ));
 6091
 6092    cx.update_editor(|editor, window, cx| {
 6093        editor.redo_selection(&Default::default(), window, cx);
 6094    });
 6095
 6096    cx.assert_editor_state(indoc!(
 6097        r#"abc
 6098           defˇghi
 6099
 6100           jk
 6101           nlmo
 6102           "#
 6103    ));
 6104
 6105    cx.update_editor(|editor, window, cx| {
 6106        editor.add_selection_below(&Default::default(), window, cx);
 6107    });
 6108
 6109    cx.assert_editor_state(indoc!(
 6110        r#"abc
 6111           defˇghi
 6112           ˇ
 6113           jk
 6114           nlmo
 6115           "#
 6116    ));
 6117
 6118    cx.update_editor(|editor, window, cx| {
 6119        editor.add_selection_below(&Default::default(), window, cx);
 6120    });
 6121
 6122    cx.assert_editor_state(indoc!(
 6123        r#"abc
 6124           defˇghi
 6125           ˇ
 6126           jkˇ
 6127           nlmo
 6128           "#
 6129    ));
 6130
 6131    cx.update_editor(|editor, window, cx| {
 6132        editor.add_selection_below(&Default::default(), window, cx);
 6133    });
 6134
 6135    cx.assert_editor_state(indoc!(
 6136        r#"abc
 6137           defˇghi
 6138           ˇ
 6139           jkˇ
 6140           nlmˇo
 6141           "#
 6142    ));
 6143
 6144    cx.update_editor(|editor, window, cx| {
 6145        editor.add_selection_below(&Default::default(), window, cx);
 6146    });
 6147
 6148    cx.assert_editor_state(indoc!(
 6149        r#"abc
 6150           defˇghi
 6151           ˇ
 6152           jkˇ
 6153           nlmˇo
 6154           ˇ"#
 6155    ));
 6156
 6157    // change selections
 6158    cx.set_state(indoc!(
 6159        r#"abc
 6160           def«ˇg»hi
 6161
 6162           jk
 6163           nlmo
 6164           "#
 6165    ));
 6166
 6167    cx.update_editor(|editor, window, cx| {
 6168        editor.add_selection_below(&Default::default(), window, cx);
 6169    });
 6170
 6171    cx.assert_editor_state(indoc!(
 6172        r#"abc
 6173           def«ˇg»hi
 6174
 6175           jk
 6176           nlm«ˇo»
 6177           "#
 6178    ));
 6179
 6180    cx.update_editor(|editor, window, cx| {
 6181        editor.add_selection_below(&Default::default(), window, cx);
 6182    });
 6183
 6184    cx.assert_editor_state(indoc!(
 6185        r#"abc
 6186           def«ˇg»hi
 6187
 6188           jk
 6189           nlm«ˇo»
 6190           "#
 6191    ));
 6192
 6193    cx.update_editor(|editor, window, cx| {
 6194        editor.add_selection_above(&Default::default(), window, cx);
 6195    });
 6196
 6197    cx.assert_editor_state(indoc!(
 6198        r#"abc
 6199           def«ˇg»hi
 6200
 6201           jk
 6202           nlmo
 6203           "#
 6204    ));
 6205
 6206    cx.update_editor(|editor, window, cx| {
 6207        editor.add_selection_above(&Default::default(), window, cx);
 6208    });
 6209
 6210    cx.assert_editor_state(indoc!(
 6211        r#"abc
 6212           def«ˇg»hi
 6213
 6214           jk
 6215           nlmo
 6216           "#
 6217    ));
 6218
 6219    // Change selections again
 6220    cx.set_state(indoc!(
 6221        r#"a«bc
 6222           defgˇ»hi
 6223
 6224           jk
 6225           nlmo
 6226           "#
 6227    ));
 6228
 6229    cx.update_editor(|editor, window, cx| {
 6230        editor.add_selection_below(&Default::default(), window, cx);
 6231    });
 6232
 6233    cx.assert_editor_state(indoc!(
 6234        r#"a«bcˇ»
 6235           d«efgˇ»hi
 6236
 6237           j«kˇ»
 6238           nlmo
 6239           "#
 6240    ));
 6241
 6242    cx.update_editor(|editor, window, cx| {
 6243        editor.add_selection_below(&Default::default(), window, cx);
 6244    });
 6245    cx.assert_editor_state(indoc!(
 6246        r#"a«bcˇ»
 6247           d«efgˇ»hi
 6248
 6249           j«kˇ»
 6250           n«lmoˇ»
 6251           "#
 6252    ));
 6253    cx.update_editor(|editor, window, cx| {
 6254        editor.add_selection_above(&Default::default(), window, cx);
 6255    });
 6256
 6257    cx.assert_editor_state(indoc!(
 6258        r#"a«bcˇ»
 6259           d«efgˇ»hi
 6260
 6261           j«kˇ»
 6262           nlmo
 6263           "#
 6264    ));
 6265
 6266    // Change selections again
 6267    cx.set_state(indoc!(
 6268        r#"abc
 6269           d«ˇefghi
 6270
 6271           jk
 6272           nlm»o
 6273           "#
 6274    ));
 6275
 6276    cx.update_editor(|editor, window, cx| {
 6277        editor.add_selection_above(&Default::default(), window, cx);
 6278    });
 6279
 6280    cx.assert_editor_state(indoc!(
 6281        r#"a«ˇbc»
 6282           d«ˇef»ghi
 6283
 6284           j«ˇk»
 6285           n«ˇlm»o
 6286           "#
 6287    ));
 6288
 6289    cx.update_editor(|editor, window, cx| {
 6290        editor.add_selection_below(&Default::default(), window, cx);
 6291    });
 6292
 6293    cx.assert_editor_state(indoc!(
 6294        r#"abc
 6295           d«ˇef»ghi
 6296
 6297           j«ˇk»
 6298           n«ˇlm»o
 6299           "#
 6300    ));
 6301}
 6302
 6303#[gpui::test]
 6304async fn test_add_selection_above_below_multi_cursor(cx: &mut TestAppContext) {
 6305    init_test(cx, |_| {});
 6306    let mut cx = EditorTestContext::new(cx).await;
 6307
 6308    cx.set_state(indoc!(
 6309        r#"line onˇe
 6310           liˇne two
 6311           line three
 6312           line four"#
 6313    ));
 6314
 6315    cx.update_editor(|editor, window, cx| {
 6316        editor.add_selection_below(&Default::default(), window, cx);
 6317    });
 6318
 6319    // test multiple cursors expand in the same direction
 6320    cx.assert_editor_state(indoc!(
 6321        r#"line onˇe
 6322           liˇne twˇo
 6323           liˇne three
 6324           line four"#
 6325    ));
 6326
 6327    cx.update_editor(|editor, window, cx| {
 6328        editor.add_selection_below(&Default::default(), window, cx);
 6329    });
 6330
 6331    cx.update_editor(|editor, window, cx| {
 6332        editor.add_selection_below(&Default::default(), window, cx);
 6333    });
 6334
 6335    // test multiple cursors expand below overflow
 6336    cx.assert_editor_state(indoc!(
 6337        r#"line onˇe
 6338           liˇne twˇo
 6339           liˇne thˇree
 6340           liˇne foˇur"#
 6341    ));
 6342
 6343    cx.update_editor(|editor, window, cx| {
 6344        editor.add_selection_above(&Default::default(), window, cx);
 6345    });
 6346
 6347    // test multiple cursors retrieves back correctly
 6348    cx.assert_editor_state(indoc!(
 6349        r#"line onˇe
 6350           liˇne twˇo
 6351           liˇne thˇree
 6352           line four"#
 6353    ));
 6354
 6355    cx.update_editor(|editor, window, cx| {
 6356        editor.add_selection_above(&Default::default(), window, cx);
 6357    });
 6358
 6359    cx.update_editor(|editor, window, cx| {
 6360        editor.add_selection_above(&Default::default(), window, cx);
 6361    });
 6362
 6363    // test multiple cursor groups maintain independent direction - first expands up, second shrinks above
 6364    cx.assert_editor_state(indoc!(
 6365        r#"liˇne onˇe
 6366           liˇne two
 6367           line three
 6368           line four"#
 6369    ));
 6370
 6371    cx.update_editor(|editor, window, cx| {
 6372        editor.undo_selection(&Default::default(), window, cx);
 6373    });
 6374
 6375    // test undo
 6376    cx.assert_editor_state(indoc!(
 6377        r#"line onˇe
 6378           liˇne twˇo
 6379           line three
 6380           line four"#
 6381    ));
 6382
 6383    cx.update_editor(|editor, window, cx| {
 6384        editor.redo_selection(&Default::default(), window, cx);
 6385    });
 6386
 6387    // test redo
 6388    cx.assert_editor_state(indoc!(
 6389        r#"liˇne onˇe
 6390           liˇne two
 6391           line three
 6392           line four"#
 6393    ));
 6394
 6395    cx.set_state(indoc!(
 6396        r#"abcd
 6397           ef«ghˇ»
 6398           ijkl
 6399           «mˇ»nop"#
 6400    ));
 6401
 6402    cx.update_editor(|editor, window, cx| {
 6403        editor.add_selection_above(&Default::default(), window, cx);
 6404    });
 6405
 6406    // test multiple selections expand in the same direction
 6407    cx.assert_editor_state(indoc!(
 6408        r#"ab«cdˇ»
 6409           ef«ghˇ»
 6410           «iˇ»jkl
 6411           «mˇ»nop"#
 6412    ));
 6413
 6414    cx.update_editor(|editor, window, cx| {
 6415        editor.add_selection_above(&Default::default(), window, cx);
 6416    });
 6417
 6418    // test multiple selection upward overflow
 6419    cx.assert_editor_state(indoc!(
 6420        r#"ab«cdˇ»
 6421           «eˇ»f«ghˇ»
 6422           «iˇ»jkl
 6423           «mˇ»nop"#
 6424    ));
 6425
 6426    cx.update_editor(|editor, window, cx| {
 6427        editor.add_selection_below(&Default::default(), window, cx);
 6428    });
 6429
 6430    // test multiple selection retrieves back correctly
 6431    cx.assert_editor_state(indoc!(
 6432        r#"abcd
 6433           ef«ghˇ»
 6434           «iˇ»jkl
 6435           «mˇ»nop"#
 6436    ));
 6437
 6438    cx.update_editor(|editor, window, cx| {
 6439        editor.add_selection_below(&Default::default(), window, cx);
 6440    });
 6441
 6442    // test multiple cursor groups maintain independent direction - first shrinks down, second expands below
 6443    cx.assert_editor_state(indoc!(
 6444        r#"abcd
 6445           ef«ghˇ»
 6446           ij«klˇ»
 6447           «mˇ»nop"#
 6448    ));
 6449
 6450    cx.update_editor(|editor, window, cx| {
 6451        editor.undo_selection(&Default::default(), window, cx);
 6452    });
 6453
 6454    // test undo
 6455    cx.assert_editor_state(indoc!(
 6456        r#"abcd
 6457           ef«ghˇ»
 6458           «iˇ»jkl
 6459           «mˇ»nop"#
 6460    ));
 6461
 6462    cx.update_editor(|editor, window, cx| {
 6463        editor.redo_selection(&Default::default(), window, cx);
 6464    });
 6465
 6466    // test redo
 6467    cx.assert_editor_state(indoc!(
 6468        r#"abcd
 6469           ef«ghˇ»
 6470           ij«klˇ»
 6471           «mˇ»nop"#
 6472    ));
 6473}
 6474
 6475#[gpui::test]
 6476async fn test_add_selection_above_below_multi_cursor_existing_state(cx: &mut TestAppContext) {
 6477    init_test(cx, |_| {});
 6478    let mut cx = EditorTestContext::new(cx).await;
 6479
 6480    cx.set_state(indoc!(
 6481        r#"line onˇe
 6482           liˇne two
 6483           line three
 6484           line four"#
 6485    ));
 6486
 6487    cx.update_editor(|editor, window, cx| {
 6488        editor.add_selection_below(&Default::default(), window, cx);
 6489        editor.add_selection_below(&Default::default(), window, cx);
 6490        editor.add_selection_below(&Default::default(), window, cx);
 6491    });
 6492
 6493    // initial state with two multi cursor groups
 6494    cx.assert_editor_state(indoc!(
 6495        r#"line onˇe
 6496           liˇne twˇo
 6497           liˇne thˇree
 6498           liˇne foˇur"#
 6499    ));
 6500
 6501    // add single cursor in middle - simulate opt click
 6502    cx.update_editor(|editor, window, cx| {
 6503        let new_cursor_point = DisplayPoint::new(DisplayRow(2), 4);
 6504        editor.begin_selection(new_cursor_point, true, 1, window, cx);
 6505        editor.end_selection(window, cx);
 6506    });
 6507
 6508    cx.assert_editor_state(indoc!(
 6509        r#"line onˇe
 6510           liˇne twˇo
 6511           liˇneˇ thˇree
 6512           liˇne foˇur"#
 6513    ));
 6514
 6515    cx.update_editor(|editor, window, cx| {
 6516        editor.add_selection_above(&Default::default(), window, cx);
 6517    });
 6518
 6519    // test new added selection expands above and existing selection shrinks
 6520    cx.assert_editor_state(indoc!(
 6521        r#"line onˇe
 6522           liˇneˇ twˇo
 6523           liˇneˇ thˇree
 6524           line four"#
 6525    ));
 6526
 6527    cx.update_editor(|editor, window, cx| {
 6528        editor.add_selection_above(&Default::default(), window, cx);
 6529    });
 6530
 6531    // test new added selection expands above and existing selection shrinks
 6532    cx.assert_editor_state(indoc!(
 6533        r#"lineˇ onˇe
 6534           liˇneˇ twˇo
 6535           lineˇ three
 6536           line four"#
 6537    ));
 6538
 6539    // intial state with two selection groups
 6540    cx.set_state(indoc!(
 6541        r#"abcd
 6542           ef«ghˇ»
 6543           ijkl
 6544           «mˇ»nop"#
 6545    ));
 6546
 6547    cx.update_editor(|editor, window, cx| {
 6548        editor.add_selection_above(&Default::default(), window, cx);
 6549        editor.add_selection_above(&Default::default(), window, cx);
 6550    });
 6551
 6552    cx.assert_editor_state(indoc!(
 6553        r#"ab«cdˇ»
 6554           «eˇ»f«ghˇ»
 6555           «iˇ»jkl
 6556           «mˇ»nop"#
 6557    ));
 6558
 6559    // add single selection in middle - simulate opt drag
 6560    cx.update_editor(|editor, window, cx| {
 6561        let new_cursor_point = DisplayPoint::new(DisplayRow(2), 3);
 6562        editor.begin_selection(new_cursor_point, true, 1, window, cx);
 6563        editor.update_selection(
 6564            DisplayPoint::new(DisplayRow(2), 4),
 6565            0,
 6566            gpui::Point::<f32>::default(),
 6567            window,
 6568            cx,
 6569        );
 6570        editor.end_selection(window, cx);
 6571    });
 6572
 6573    cx.assert_editor_state(indoc!(
 6574        r#"ab«cdˇ»
 6575           «eˇ»f«ghˇ»
 6576           «iˇ»jk«lˇ»
 6577           «mˇ»nop"#
 6578    ));
 6579
 6580    cx.update_editor(|editor, window, cx| {
 6581        editor.add_selection_below(&Default::default(), window, cx);
 6582    });
 6583
 6584    // test new added selection expands below, others shrinks from above
 6585    cx.assert_editor_state(indoc!(
 6586        r#"abcd
 6587           ef«ghˇ»
 6588           «iˇ»jk«lˇ»
 6589           «mˇ»no«pˇ»"#
 6590    ));
 6591}
 6592
 6593#[gpui::test]
 6594async fn test_select_next(cx: &mut TestAppContext) {
 6595    init_test(cx, |_| {});
 6596
 6597    let mut cx = EditorTestContext::new(cx).await;
 6598    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6599
 6600    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6601        .unwrap();
 6602    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6603
 6604    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6605        .unwrap();
 6606    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6607
 6608    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6609    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6610
 6611    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6612    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6613
 6614    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6615        .unwrap();
 6616    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6617
 6618    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6619        .unwrap();
 6620    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6621
 6622    // Test selection direction should be preserved
 6623    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6624
 6625    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6626        .unwrap();
 6627    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6628}
 6629
 6630#[gpui::test]
 6631async fn test_select_all_matches(cx: &mut TestAppContext) {
 6632    init_test(cx, |_| {});
 6633
 6634    let mut cx = EditorTestContext::new(cx).await;
 6635
 6636    // Test caret-only selections
 6637    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6638    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6639        .unwrap();
 6640    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6641
 6642    // Test left-to-right selections
 6643    cx.set_state("abc\n«abcˇ»\nabc");
 6644    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6645        .unwrap();
 6646    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6647
 6648    // Test right-to-left selections
 6649    cx.set_state("abc\n«ˇabc»\nabc");
 6650    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6651        .unwrap();
 6652    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6653
 6654    // Test selecting whitespace with caret selection
 6655    cx.set_state("abc\nˇ   abc\nabc");
 6656    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6657        .unwrap();
 6658    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6659
 6660    // Test selecting whitespace with left-to-right selection
 6661    cx.set_state("abc\n«ˇ  »abc\nabc");
 6662    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6663        .unwrap();
 6664    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6665
 6666    // Test no matches with right-to-left selection
 6667    cx.set_state("abc\n«  ˇ»abc\nabc");
 6668    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6669        .unwrap();
 6670    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6671}
 6672
 6673#[gpui::test]
 6674async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6675    init_test(cx, |_| {});
 6676
 6677    let mut cx = EditorTestContext::new(cx).await;
 6678
 6679    let large_body_1 = "\nd".repeat(200);
 6680    let large_body_2 = "\ne".repeat(200);
 6681
 6682    cx.set_state(&format!(
 6683        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6684    ));
 6685    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6686        let scroll_position = editor.scroll_position(cx);
 6687        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6688        scroll_position
 6689    });
 6690
 6691    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6692        .unwrap();
 6693    cx.assert_editor_state(&format!(
 6694        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6695    ));
 6696    let scroll_position_after_selection =
 6697        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6698    assert_eq!(
 6699        initial_scroll_position, scroll_position_after_selection,
 6700        "Scroll position should not change after selecting all matches"
 6701    );
 6702}
 6703
 6704#[gpui::test]
 6705async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6706    init_test(cx, |_| {});
 6707
 6708    let mut cx = EditorLspTestContext::new_rust(
 6709        lsp::ServerCapabilities {
 6710            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6711            ..Default::default()
 6712        },
 6713        cx,
 6714    )
 6715    .await;
 6716
 6717    cx.set_state(indoc! {"
 6718        line 1
 6719        line 2
 6720        linˇe 3
 6721        line 4
 6722        line 5
 6723    "});
 6724
 6725    // Make an edit
 6726    cx.update_editor(|editor, window, cx| {
 6727        editor.handle_input("X", window, cx);
 6728    });
 6729
 6730    // Move cursor to a different position
 6731    cx.update_editor(|editor, window, cx| {
 6732        editor.change_selections(None, window, cx, |s| {
 6733            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6734        });
 6735    });
 6736
 6737    cx.assert_editor_state(indoc! {"
 6738        line 1
 6739        line 2
 6740        linXe 3
 6741        line 4
 6742        liˇne 5
 6743    "});
 6744
 6745    cx.lsp
 6746        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6747            Ok(Some(vec![lsp::TextEdit::new(
 6748                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6749                "PREFIX ".to_string(),
 6750            )]))
 6751        });
 6752
 6753    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6754        .unwrap()
 6755        .await
 6756        .unwrap();
 6757
 6758    cx.assert_editor_state(indoc! {"
 6759        PREFIX line 1
 6760        line 2
 6761        linXe 3
 6762        line 4
 6763        liˇne 5
 6764    "});
 6765
 6766    // Undo formatting
 6767    cx.update_editor(|editor, window, cx| {
 6768        editor.undo(&Default::default(), window, cx);
 6769    });
 6770
 6771    // Verify cursor moved back to position after edit
 6772    cx.assert_editor_state(indoc! {"
 6773        line 1
 6774        line 2
 6775        linXˇe 3
 6776        line 4
 6777        line 5
 6778    "});
 6779}
 6780
 6781#[gpui::test]
 6782async fn test_undo_inline_completion_scrolls_to_edit_pos(cx: &mut TestAppContext) {
 6783    init_test(cx, |_| {});
 6784
 6785    let mut cx = EditorTestContext::new(cx).await;
 6786
 6787    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
 6788    cx.update_editor(|editor, window, cx| {
 6789        editor.set_edit_prediction_provider(Some(provider.clone()), window, cx);
 6790    });
 6791
 6792    cx.set_state(indoc! {"
 6793        line 1
 6794        line 2
 6795        linˇe 3
 6796        line 4
 6797        line 5
 6798        line 6
 6799        line 7
 6800        line 8
 6801        line 9
 6802        line 10
 6803    "});
 6804
 6805    let snapshot = cx.buffer_snapshot();
 6806    let edit_position = snapshot.anchor_after(Point::new(2, 4));
 6807
 6808    cx.update(|_, cx| {
 6809        provider.update(cx, |provider, _| {
 6810            provider.set_inline_completion(Some(inline_completion::InlineCompletion {
 6811                id: None,
 6812                edits: vec![(edit_position..edit_position, "X".into())],
 6813                edit_preview: None,
 6814            }))
 6815        })
 6816    });
 6817
 6818    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 6819    cx.update_editor(|editor, window, cx| {
 6820        editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)
 6821    });
 6822
 6823    cx.assert_editor_state(indoc! {"
 6824        line 1
 6825        line 2
 6826        lineXˇ 3
 6827        line 4
 6828        line 5
 6829        line 6
 6830        line 7
 6831        line 8
 6832        line 9
 6833        line 10
 6834    "});
 6835
 6836    cx.update_editor(|editor, window, cx| {
 6837        editor.change_selections(None, window, cx, |s| {
 6838            s.select_ranges([Point::new(9, 2)..Point::new(9, 2)]);
 6839        });
 6840    });
 6841
 6842    cx.assert_editor_state(indoc! {"
 6843        line 1
 6844        line 2
 6845        lineX 3
 6846        line 4
 6847        line 5
 6848        line 6
 6849        line 7
 6850        line 8
 6851        line 9
 6852        liˇne 10
 6853    "});
 6854
 6855    cx.update_editor(|editor, window, cx| {
 6856        editor.undo(&Default::default(), window, cx);
 6857    });
 6858
 6859    cx.assert_editor_state(indoc! {"
 6860        line 1
 6861        line 2
 6862        lineˇ 3
 6863        line 4
 6864        line 5
 6865        line 6
 6866        line 7
 6867        line 8
 6868        line 9
 6869        line 10
 6870    "});
 6871}
 6872
 6873#[gpui::test]
 6874async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6875    init_test(cx, |_| {});
 6876
 6877    let mut cx = EditorTestContext::new(cx).await;
 6878    cx.set_state(
 6879        r#"let foo = 2;
 6880lˇet foo = 2;
 6881let fooˇ = 2;
 6882let foo = 2;
 6883let foo = ˇ2;"#,
 6884    );
 6885
 6886    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6887        .unwrap();
 6888    cx.assert_editor_state(
 6889        r#"let foo = 2;
 6890«letˇ» foo = 2;
 6891let «fooˇ» = 2;
 6892let foo = 2;
 6893let foo = «2ˇ»;"#,
 6894    );
 6895
 6896    // noop for multiple selections with different contents
 6897    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6898        .unwrap();
 6899    cx.assert_editor_state(
 6900        r#"let foo = 2;
 6901«letˇ» foo = 2;
 6902let «fooˇ» = 2;
 6903let foo = 2;
 6904let foo = «2ˇ»;"#,
 6905    );
 6906
 6907    // Test last selection direction should be preserved
 6908    cx.set_state(
 6909        r#"let foo = 2;
 6910let foo = 2;
 6911let «fooˇ» = 2;
 6912let «ˇfoo» = 2;
 6913let foo = 2;"#,
 6914    );
 6915
 6916    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6917        .unwrap();
 6918    cx.assert_editor_state(
 6919        r#"let foo = 2;
 6920let foo = 2;
 6921let «fooˇ» = 2;
 6922let «ˇfoo» = 2;
 6923let «ˇfoo» = 2;"#,
 6924    );
 6925}
 6926
 6927#[gpui::test]
 6928async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6929    init_test(cx, |_| {});
 6930
 6931    let mut cx =
 6932        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6933
 6934    cx.assert_editor_state(indoc! {"
 6935        ˇbbb
 6936        ccc
 6937
 6938        bbb
 6939        ccc
 6940        "});
 6941    cx.dispatch_action(SelectPrevious::default());
 6942    cx.assert_editor_state(indoc! {"
 6943                «bbbˇ»
 6944                ccc
 6945
 6946                bbb
 6947                ccc
 6948                "});
 6949    cx.dispatch_action(SelectPrevious::default());
 6950    cx.assert_editor_state(indoc! {"
 6951                «bbbˇ»
 6952                ccc
 6953
 6954                «bbbˇ»
 6955                ccc
 6956                "});
 6957}
 6958
 6959#[gpui::test]
 6960async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6961    init_test(cx, |_| {});
 6962
 6963    let mut cx = EditorTestContext::new(cx).await;
 6964    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6965
 6966    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6967        .unwrap();
 6968    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6969
 6970    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6971        .unwrap();
 6972    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6973
 6974    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6975    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6976
 6977    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6978    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6979
 6980    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6981        .unwrap();
 6982    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6983
 6984    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6985        .unwrap();
 6986    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6987}
 6988
 6989#[gpui::test]
 6990async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6991    init_test(cx, |_| {});
 6992
 6993    let mut cx = EditorTestContext::new(cx).await;
 6994    cx.set_state("");
 6995
 6996    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6997        .unwrap();
 6998    cx.assert_editor_state("«aˇ»");
 6999    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7000        .unwrap();
 7001    cx.assert_editor_state("«aˇ»");
 7002}
 7003
 7004#[gpui::test]
 7005async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 7006    init_test(cx, |_| {});
 7007
 7008    let mut cx = EditorTestContext::new(cx).await;
 7009    cx.set_state(
 7010        r#"let foo = 2;
 7011lˇet foo = 2;
 7012let fooˇ = 2;
 7013let foo = 2;
 7014let foo = ˇ2;"#,
 7015    );
 7016
 7017    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7018        .unwrap();
 7019    cx.assert_editor_state(
 7020        r#"let foo = 2;
 7021«letˇ» foo = 2;
 7022let «fooˇ» = 2;
 7023let foo = 2;
 7024let foo = «2ˇ»;"#,
 7025    );
 7026
 7027    // noop for multiple selections with different contents
 7028    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7029        .unwrap();
 7030    cx.assert_editor_state(
 7031        r#"let foo = 2;
 7032«letˇ» foo = 2;
 7033let «fooˇ» = 2;
 7034let foo = 2;
 7035let foo = «2ˇ»;"#,
 7036    );
 7037}
 7038
 7039#[gpui::test]
 7040async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 7041    init_test(cx, |_| {});
 7042
 7043    let mut cx = EditorTestContext::new(cx).await;
 7044    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 7045
 7046    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7047        .unwrap();
 7048    // selection direction is preserved
 7049    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 7050
 7051    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7052        .unwrap();
 7053    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 7054
 7055    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 7056    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 7057
 7058    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 7059    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 7060
 7061    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7062        .unwrap();
 7063    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 7064
 7065    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 7066        .unwrap();
 7067    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 7068}
 7069
 7070#[gpui::test]
 7071async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 7072    init_test(cx, |_| {});
 7073
 7074    let language = Arc::new(Language::new(
 7075        LanguageConfig::default(),
 7076        Some(tree_sitter_rust::LANGUAGE.into()),
 7077    ));
 7078
 7079    let text = r#"
 7080        use mod1::mod2::{mod3, mod4};
 7081
 7082        fn fn_1(param1: bool, param2: &str) {
 7083            let var1 = "text";
 7084        }
 7085    "#
 7086    .unindent();
 7087
 7088    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7089    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7090    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7091
 7092    editor
 7093        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7094        .await;
 7095
 7096    editor.update_in(cx, |editor, window, cx| {
 7097        editor.change_selections(None, window, cx, |s| {
 7098            s.select_display_ranges([
 7099                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 7100                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 7101                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 7102            ]);
 7103        });
 7104        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7105    });
 7106    editor.update(cx, |editor, cx| {
 7107        assert_text_with_selections(
 7108            editor,
 7109            indoc! {r#"
 7110                use mod1::mod2::{mod3, «mod4ˇ»};
 7111
 7112                fn fn_1«ˇ(param1: bool, param2: &str)» {
 7113                    let var1 = "«ˇtext»";
 7114                }
 7115            "#},
 7116            cx,
 7117        );
 7118    });
 7119
 7120    editor.update_in(cx, |editor, window, cx| {
 7121        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7122    });
 7123    editor.update(cx, |editor, cx| {
 7124        assert_text_with_selections(
 7125            editor,
 7126            indoc! {r#"
 7127                use mod1::mod2::«{mod3, mod4}ˇ»;
 7128
 7129                «ˇfn fn_1(param1: bool, param2: &str) {
 7130                    let var1 = "text";
 7131 7132            "#},
 7133            cx,
 7134        );
 7135    });
 7136
 7137    editor.update_in(cx, |editor, window, cx| {
 7138        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7139    });
 7140    assert_eq!(
 7141        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 7142        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 7143    );
 7144
 7145    // Trying to expand the selected syntax node one more time has no effect.
 7146    editor.update_in(cx, |editor, window, cx| {
 7147        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7148    });
 7149    assert_eq!(
 7150        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 7151        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 7152    );
 7153
 7154    editor.update_in(cx, |editor, window, cx| {
 7155        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 7156    });
 7157    editor.update(cx, |editor, cx| {
 7158        assert_text_with_selections(
 7159            editor,
 7160            indoc! {r#"
 7161                use mod1::mod2::«{mod3, mod4}ˇ»;
 7162
 7163                «ˇfn fn_1(param1: bool, param2: &str) {
 7164                    let var1 = "text";
 7165 7166            "#},
 7167            cx,
 7168        );
 7169    });
 7170
 7171    editor.update_in(cx, |editor, window, cx| {
 7172        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 7173    });
 7174    editor.update(cx, |editor, cx| {
 7175        assert_text_with_selections(
 7176            editor,
 7177            indoc! {r#"
 7178                use mod1::mod2::{mod3, «mod4ˇ»};
 7179
 7180                fn fn_1«ˇ(param1: bool, param2: &str)» {
 7181                    let var1 = "«ˇtext»";
 7182                }
 7183            "#},
 7184            cx,
 7185        );
 7186    });
 7187
 7188    editor.update_in(cx, |editor, window, cx| {
 7189        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 7190    });
 7191    editor.update(cx, |editor, cx| {
 7192        assert_text_with_selections(
 7193            editor,
 7194            indoc! {r#"
 7195                use mod1::mod2::{mod3, mo«ˇ»d4};
 7196
 7197                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 7198                    let var1 = "te«ˇ»xt";
 7199                }
 7200            "#},
 7201            cx,
 7202        );
 7203    });
 7204
 7205    // Trying to shrink the selected syntax node one more time has no effect.
 7206    editor.update_in(cx, |editor, window, cx| {
 7207        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 7208    });
 7209    editor.update_in(cx, |editor, _, cx| {
 7210        assert_text_with_selections(
 7211            editor,
 7212            indoc! {r#"
 7213                use mod1::mod2::{mod3, mo«ˇ»d4};
 7214
 7215                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 7216                    let var1 = "te«ˇ»xt";
 7217                }
 7218            "#},
 7219            cx,
 7220        );
 7221    });
 7222
 7223    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 7224    // a fold.
 7225    editor.update_in(cx, |editor, window, cx| {
 7226        editor.fold_creases(
 7227            vec![
 7228                Crease::simple(
 7229                    Point::new(0, 21)..Point::new(0, 24),
 7230                    FoldPlaceholder::test(),
 7231                ),
 7232                Crease::simple(
 7233                    Point::new(3, 20)..Point::new(3, 22),
 7234                    FoldPlaceholder::test(),
 7235                ),
 7236            ],
 7237            true,
 7238            window,
 7239            cx,
 7240        );
 7241        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7242    });
 7243    editor.update(cx, |editor, cx| {
 7244        assert_text_with_selections(
 7245            editor,
 7246            indoc! {r#"
 7247                use mod1::mod2::«{mod3, mod4}ˇ»;
 7248
 7249                fn fn_1«ˇ(param1: bool, param2: &str)» {
 7250                    let var1 = "«ˇtext»";
 7251                }
 7252            "#},
 7253            cx,
 7254        );
 7255    });
 7256}
 7257
 7258#[gpui::test]
 7259async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 7260    init_test(cx, |_| {});
 7261
 7262    let language = Arc::new(Language::new(
 7263        LanguageConfig::default(),
 7264        Some(tree_sitter_rust::LANGUAGE.into()),
 7265    ));
 7266
 7267    let text = "let a = 2;";
 7268
 7269    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7270    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7271    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7272
 7273    editor
 7274        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7275        .await;
 7276
 7277    // Test case 1: Cursor at end of word
 7278    editor.update_in(cx, |editor, window, cx| {
 7279        editor.change_selections(None, window, cx, |s| {
 7280            s.select_display_ranges([
 7281                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 7282            ]);
 7283        });
 7284    });
 7285    editor.update(cx, |editor, cx| {
 7286        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 7287    });
 7288    editor.update_in(cx, |editor, window, cx| {
 7289        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7290    });
 7291    editor.update(cx, |editor, cx| {
 7292        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 7293    });
 7294    editor.update_in(cx, |editor, window, cx| {
 7295        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7296    });
 7297    editor.update(cx, |editor, cx| {
 7298        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 7299    });
 7300
 7301    // Test case 2: Cursor at end of statement
 7302    editor.update_in(cx, |editor, window, cx| {
 7303        editor.change_selections(None, window, cx, |s| {
 7304            s.select_display_ranges([
 7305                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 7306            ]);
 7307        });
 7308    });
 7309    editor.update(cx, |editor, cx| {
 7310        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 7311    });
 7312    editor.update_in(cx, |editor, window, cx| {
 7313        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7314    });
 7315    editor.update(cx, |editor, cx| {
 7316        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 7317    });
 7318}
 7319
 7320#[gpui::test]
 7321async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 7322    init_test(cx, |_| {});
 7323
 7324    let language = Arc::new(Language::new(
 7325        LanguageConfig::default(),
 7326        Some(tree_sitter_rust::LANGUAGE.into()),
 7327    ));
 7328
 7329    let text = r#"
 7330        use mod1::mod2::{mod3, mod4};
 7331
 7332        fn fn_1(param1: bool, param2: &str) {
 7333            let var1 = "hello world";
 7334        }
 7335    "#
 7336    .unindent();
 7337
 7338    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7339    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7340    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7341
 7342    editor
 7343        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7344        .await;
 7345
 7346    // Test 1: Cursor on a letter of a string word
 7347    editor.update_in(cx, |editor, window, cx| {
 7348        editor.change_selections(None, window, cx, |s| {
 7349            s.select_display_ranges([
 7350                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 7351            ]);
 7352        });
 7353    });
 7354    editor.update_in(cx, |editor, window, cx| {
 7355        assert_text_with_selections(
 7356            editor,
 7357            indoc! {r#"
 7358                use mod1::mod2::{mod3, mod4};
 7359
 7360                fn fn_1(param1: bool, param2: &str) {
 7361                    let var1 = "hˇello world";
 7362                }
 7363            "#},
 7364            cx,
 7365        );
 7366        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7367        assert_text_with_selections(
 7368            editor,
 7369            indoc! {r#"
 7370                use mod1::mod2::{mod3, mod4};
 7371
 7372                fn fn_1(param1: bool, param2: &str) {
 7373                    let var1 = "«ˇhello» world";
 7374                }
 7375            "#},
 7376            cx,
 7377        );
 7378    });
 7379
 7380    // Test 2: Partial selection within a word
 7381    editor.update_in(cx, |editor, window, cx| {
 7382        editor.change_selections(None, window, cx, |s| {
 7383            s.select_display_ranges([
 7384                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 7385            ]);
 7386        });
 7387    });
 7388    editor.update_in(cx, |editor, window, cx| {
 7389        assert_text_with_selections(
 7390            editor,
 7391            indoc! {r#"
 7392                use mod1::mod2::{mod3, mod4};
 7393
 7394                fn fn_1(param1: bool, param2: &str) {
 7395                    let var1 = "h«elˇ»lo world";
 7396                }
 7397            "#},
 7398            cx,
 7399        );
 7400        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7401        assert_text_with_selections(
 7402            editor,
 7403            indoc! {r#"
 7404                use mod1::mod2::{mod3, mod4};
 7405
 7406                fn fn_1(param1: bool, param2: &str) {
 7407                    let var1 = "«ˇhello» world";
 7408                }
 7409            "#},
 7410            cx,
 7411        );
 7412    });
 7413
 7414    // Test 3: Complete word already selected
 7415    editor.update_in(cx, |editor, window, cx| {
 7416        editor.change_selections(None, window, cx, |s| {
 7417            s.select_display_ranges([
 7418                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 7419            ]);
 7420        });
 7421    });
 7422    editor.update_in(cx, |editor, window, cx| {
 7423        assert_text_with_selections(
 7424            editor,
 7425            indoc! {r#"
 7426                use mod1::mod2::{mod3, mod4};
 7427
 7428                fn fn_1(param1: bool, param2: &str) {
 7429                    let var1 = "«helloˇ» world";
 7430                }
 7431            "#},
 7432            cx,
 7433        );
 7434        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7435        assert_text_with_selections(
 7436            editor,
 7437            indoc! {r#"
 7438                use mod1::mod2::{mod3, mod4};
 7439
 7440                fn fn_1(param1: bool, param2: &str) {
 7441                    let var1 = "«hello worldˇ»";
 7442                }
 7443            "#},
 7444            cx,
 7445        );
 7446    });
 7447
 7448    // Test 4: Selection spanning across words
 7449    editor.update_in(cx, |editor, window, cx| {
 7450        editor.change_selections(None, window, cx, |s| {
 7451            s.select_display_ranges([
 7452                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 7453            ]);
 7454        });
 7455    });
 7456    editor.update_in(cx, |editor, window, cx| {
 7457        assert_text_with_selections(
 7458            editor,
 7459            indoc! {r#"
 7460                use mod1::mod2::{mod3, mod4};
 7461
 7462                fn fn_1(param1: bool, param2: &str) {
 7463                    let var1 = "hel«lo woˇ»rld";
 7464                }
 7465            "#},
 7466            cx,
 7467        );
 7468        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7469        assert_text_with_selections(
 7470            editor,
 7471            indoc! {r#"
 7472                use mod1::mod2::{mod3, mod4};
 7473
 7474                fn fn_1(param1: bool, param2: &str) {
 7475                    let var1 = "«ˇhello world»";
 7476                }
 7477            "#},
 7478            cx,
 7479        );
 7480    });
 7481
 7482    // Test 5: Expansion beyond string
 7483    editor.update_in(cx, |editor, window, cx| {
 7484        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7485        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7486        assert_text_with_selections(
 7487            editor,
 7488            indoc! {r#"
 7489                use mod1::mod2::{mod3, mod4};
 7490
 7491                fn fn_1(param1: bool, param2: &str) {
 7492                    «ˇlet var1 = "hello world";»
 7493                }
 7494            "#},
 7495            cx,
 7496        );
 7497    });
 7498}
 7499
 7500#[gpui::test]
 7501async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 7502    init_test(cx, |_| {});
 7503
 7504    let base_text = r#"
 7505        impl A {
 7506            // this is an uncommitted comment
 7507
 7508            fn b() {
 7509                c();
 7510            }
 7511
 7512            // this is another uncommitted comment
 7513
 7514            fn d() {
 7515                // e
 7516                // f
 7517            }
 7518        }
 7519
 7520        fn g() {
 7521            // h
 7522        }
 7523    "#
 7524    .unindent();
 7525
 7526    let text = r#"
 7527        ˇimpl A {
 7528
 7529            fn b() {
 7530                c();
 7531            }
 7532
 7533            fn d() {
 7534                // e
 7535                // f
 7536            }
 7537        }
 7538
 7539        fn g() {
 7540            // h
 7541        }
 7542    "#
 7543    .unindent();
 7544
 7545    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7546    cx.set_state(&text);
 7547    cx.set_head_text(&base_text);
 7548    cx.update_editor(|editor, window, cx| {
 7549        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7550    });
 7551
 7552    cx.assert_state_with_diff(
 7553        "
 7554        ˇimpl A {
 7555      -     // this is an uncommitted comment
 7556
 7557            fn b() {
 7558                c();
 7559            }
 7560
 7561      -     // this is another uncommitted comment
 7562      -
 7563            fn d() {
 7564                // e
 7565                // f
 7566            }
 7567        }
 7568
 7569        fn g() {
 7570            // h
 7571        }
 7572    "
 7573        .unindent(),
 7574    );
 7575
 7576    let expected_display_text = "
 7577        impl A {
 7578            // this is an uncommitted comment
 7579
 7580            fn b() {
 7581 7582            }
 7583
 7584            // this is another uncommitted comment
 7585
 7586            fn d() {
 7587 7588            }
 7589        }
 7590
 7591        fn g() {
 7592 7593        }
 7594        "
 7595    .unindent();
 7596
 7597    cx.update_editor(|editor, window, cx| {
 7598        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7599        assert_eq!(editor.display_text(cx), expected_display_text);
 7600    });
 7601}
 7602
 7603#[gpui::test]
 7604async fn test_autoindent(cx: &mut TestAppContext) {
 7605    init_test(cx, |_| {});
 7606
 7607    let language = Arc::new(
 7608        Language::new(
 7609            LanguageConfig {
 7610                brackets: BracketPairConfig {
 7611                    pairs: vec![
 7612                        BracketPair {
 7613                            start: "{".to_string(),
 7614                            end: "}".to_string(),
 7615                            close: false,
 7616                            surround: false,
 7617                            newline: true,
 7618                        },
 7619                        BracketPair {
 7620                            start: "(".to_string(),
 7621                            end: ")".to_string(),
 7622                            close: false,
 7623                            surround: false,
 7624                            newline: true,
 7625                        },
 7626                    ],
 7627                    ..Default::default()
 7628                },
 7629                ..Default::default()
 7630            },
 7631            Some(tree_sitter_rust::LANGUAGE.into()),
 7632        )
 7633        .with_indents_query(
 7634            r#"
 7635                (_ "(" ")" @end) @indent
 7636                (_ "{" "}" @end) @indent
 7637            "#,
 7638        )
 7639        .unwrap(),
 7640    );
 7641
 7642    let text = "fn a() {}";
 7643
 7644    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7645    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7646    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7647    editor
 7648        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7649        .await;
 7650
 7651    editor.update_in(cx, |editor, window, cx| {
 7652        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7653        editor.newline(&Newline, window, cx);
 7654        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7655        assert_eq!(
 7656            editor.selections.ranges(cx),
 7657            &[
 7658                Point::new(1, 4)..Point::new(1, 4),
 7659                Point::new(3, 4)..Point::new(3, 4),
 7660                Point::new(5, 0)..Point::new(5, 0)
 7661            ]
 7662        );
 7663    });
 7664}
 7665
 7666#[gpui::test]
 7667async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7668    init_test(cx, |_| {});
 7669
 7670    {
 7671        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7672        cx.set_state(indoc! {"
 7673            impl A {
 7674
 7675                fn b() {}
 7676
 7677            «fn c() {
 7678
 7679            }ˇ»
 7680            }
 7681        "});
 7682
 7683        cx.update_editor(|editor, window, cx| {
 7684            editor.autoindent(&Default::default(), window, cx);
 7685        });
 7686
 7687        cx.assert_editor_state(indoc! {"
 7688            impl A {
 7689
 7690                fn b() {}
 7691
 7692                «fn c() {
 7693
 7694                }ˇ»
 7695            }
 7696        "});
 7697    }
 7698
 7699    {
 7700        let mut cx = EditorTestContext::new_multibuffer(
 7701            cx,
 7702            [indoc! { "
 7703                impl A {
 7704                «
 7705                // a
 7706                fn b(){}
 7707                »
 7708                «
 7709                    }
 7710                    fn c(){}
 7711                »
 7712            "}],
 7713        );
 7714
 7715        let buffer = cx.update_editor(|editor, _, cx| {
 7716            let buffer = editor.buffer().update(cx, |buffer, _| {
 7717                buffer.all_buffers().iter().next().unwrap().clone()
 7718            });
 7719            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7720            buffer
 7721        });
 7722
 7723        cx.run_until_parked();
 7724        cx.update_editor(|editor, window, cx| {
 7725            editor.select_all(&Default::default(), window, cx);
 7726            editor.autoindent(&Default::default(), window, cx)
 7727        });
 7728        cx.run_until_parked();
 7729
 7730        cx.update(|_, cx| {
 7731            assert_eq!(
 7732                buffer.read(cx).text(),
 7733                indoc! { "
 7734                    impl A {
 7735
 7736                        // a
 7737                        fn b(){}
 7738
 7739
 7740                    }
 7741                    fn c(){}
 7742
 7743                " }
 7744            )
 7745        });
 7746    }
 7747}
 7748
 7749#[gpui::test]
 7750async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7751    init_test(cx, |_| {});
 7752
 7753    let mut cx = EditorTestContext::new(cx).await;
 7754
 7755    let language = Arc::new(Language::new(
 7756        LanguageConfig {
 7757            brackets: BracketPairConfig {
 7758                pairs: vec![
 7759                    BracketPair {
 7760                        start: "{".to_string(),
 7761                        end: "}".to_string(),
 7762                        close: true,
 7763                        surround: true,
 7764                        newline: true,
 7765                    },
 7766                    BracketPair {
 7767                        start: "(".to_string(),
 7768                        end: ")".to_string(),
 7769                        close: true,
 7770                        surround: true,
 7771                        newline: true,
 7772                    },
 7773                    BracketPair {
 7774                        start: "/*".to_string(),
 7775                        end: " */".to_string(),
 7776                        close: true,
 7777                        surround: true,
 7778                        newline: true,
 7779                    },
 7780                    BracketPair {
 7781                        start: "[".to_string(),
 7782                        end: "]".to_string(),
 7783                        close: false,
 7784                        surround: false,
 7785                        newline: true,
 7786                    },
 7787                    BracketPair {
 7788                        start: "\"".to_string(),
 7789                        end: "\"".to_string(),
 7790                        close: true,
 7791                        surround: true,
 7792                        newline: false,
 7793                    },
 7794                    BracketPair {
 7795                        start: "<".to_string(),
 7796                        end: ">".to_string(),
 7797                        close: false,
 7798                        surround: true,
 7799                        newline: true,
 7800                    },
 7801                ],
 7802                ..Default::default()
 7803            },
 7804            autoclose_before: "})]".to_string(),
 7805            ..Default::default()
 7806        },
 7807        Some(tree_sitter_rust::LANGUAGE.into()),
 7808    ));
 7809
 7810    cx.language_registry().add(language.clone());
 7811    cx.update_buffer(|buffer, cx| {
 7812        buffer.set_language(Some(language), cx);
 7813    });
 7814
 7815    cx.set_state(
 7816        &r#"
 7817            🏀ˇ
 7818            εˇ
 7819            ❤️ˇ
 7820        "#
 7821        .unindent(),
 7822    );
 7823
 7824    // autoclose multiple nested brackets at multiple cursors
 7825    cx.update_editor(|editor, window, cx| {
 7826        editor.handle_input("{", window, cx);
 7827        editor.handle_input("{", window, cx);
 7828        editor.handle_input("{", window, cx);
 7829    });
 7830    cx.assert_editor_state(
 7831        &"
 7832            🏀{{{ˇ}}}
 7833            ε{{{ˇ}}}
 7834            ❤️{{{ˇ}}}
 7835        "
 7836        .unindent(),
 7837    );
 7838
 7839    // insert a different closing bracket
 7840    cx.update_editor(|editor, window, cx| {
 7841        editor.handle_input(")", window, cx);
 7842    });
 7843    cx.assert_editor_state(
 7844        &"
 7845            🏀{{{)ˇ}}}
 7846            ε{{{)ˇ}}}
 7847            ❤️{{{)ˇ}}}
 7848        "
 7849        .unindent(),
 7850    );
 7851
 7852    // skip over the auto-closed brackets when typing a closing bracket
 7853    cx.update_editor(|editor, window, cx| {
 7854        editor.move_right(&MoveRight, window, cx);
 7855        editor.handle_input("}", window, cx);
 7856        editor.handle_input("}", window, cx);
 7857        editor.handle_input("}", window, cx);
 7858    });
 7859    cx.assert_editor_state(
 7860        &"
 7861            🏀{{{)}}}}ˇ
 7862            ε{{{)}}}}ˇ
 7863            ❤️{{{)}}}}ˇ
 7864        "
 7865        .unindent(),
 7866    );
 7867
 7868    // autoclose multi-character pairs
 7869    cx.set_state(
 7870        &"
 7871            ˇ
 7872            ˇ
 7873        "
 7874        .unindent(),
 7875    );
 7876    cx.update_editor(|editor, window, cx| {
 7877        editor.handle_input("/", window, cx);
 7878        editor.handle_input("*", window, cx);
 7879    });
 7880    cx.assert_editor_state(
 7881        &"
 7882            /*ˇ */
 7883            /*ˇ */
 7884        "
 7885        .unindent(),
 7886    );
 7887
 7888    // one cursor autocloses a multi-character pair, one cursor
 7889    // does not autoclose.
 7890    cx.set_state(
 7891        &"
 7892 7893            ˇ
 7894        "
 7895        .unindent(),
 7896    );
 7897    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7898    cx.assert_editor_state(
 7899        &"
 7900            /*ˇ */
 7901 7902        "
 7903        .unindent(),
 7904    );
 7905
 7906    // Don't autoclose if the next character isn't whitespace and isn't
 7907    // listed in the language's "autoclose_before" section.
 7908    cx.set_state("ˇa b");
 7909    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7910    cx.assert_editor_state("{ˇa b");
 7911
 7912    // Don't autoclose if `close` is false for the bracket pair
 7913    cx.set_state("ˇ");
 7914    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7915    cx.assert_editor_state("");
 7916
 7917    // Surround with brackets if text is selected
 7918    cx.set_state("«aˇ» b");
 7919    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7920    cx.assert_editor_state("{«aˇ»} b");
 7921
 7922    // Autoclose when not immediately after a word character
 7923    cx.set_state("a ˇ");
 7924    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7925    cx.assert_editor_state("a \"ˇ\"");
 7926
 7927    // Autoclose pair where the start and end characters are the same
 7928    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7929    cx.assert_editor_state("a \"\"ˇ");
 7930
 7931    // Don't autoclose when immediately after a word character
 7932    cx.set_state("");
 7933    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7934    cx.assert_editor_state("a\"ˇ");
 7935
 7936    // Do autoclose when after a non-word character
 7937    cx.set_state("");
 7938    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7939    cx.assert_editor_state("{\"ˇ\"");
 7940
 7941    // Non identical pairs autoclose regardless of preceding character
 7942    cx.set_state("");
 7943    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7944    cx.assert_editor_state("a{ˇ}");
 7945
 7946    // Don't autoclose pair if autoclose is disabled
 7947    cx.set_state("ˇ");
 7948    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7949    cx.assert_editor_state("");
 7950
 7951    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7952    cx.set_state("«aˇ» b");
 7953    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7954    cx.assert_editor_state("<«aˇ»> b");
 7955}
 7956
 7957#[gpui::test]
 7958async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7959    init_test(cx, |settings| {
 7960        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7961    });
 7962
 7963    let mut cx = EditorTestContext::new(cx).await;
 7964
 7965    let language = Arc::new(Language::new(
 7966        LanguageConfig {
 7967            brackets: BracketPairConfig {
 7968                pairs: vec![
 7969                    BracketPair {
 7970                        start: "{".to_string(),
 7971                        end: "}".to_string(),
 7972                        close: true,
 7973                        surround: true,
 7974                        newline: true,
 7975                    },
 7976                    BracketPair {
 7977                        start: "(".to_string(),
 7978                        end: ")".to_string(),
 7979                        close: true,
 7980                        surround: true,
 7981                        newline: true,
 7982                    },
 7983                    BracketPair {
 7984                        start: "[".to_string(),
 7985                        end: "]".to_string(),
 7986                        close: false,
 7987                        surround: false,
 7988                        newline: true,
 7989                    },
 7990                ],
 7991                ..Default::default()
 7992            },
 7993            autoclose_before: "})]".to_string(),
 7994            ..Default::default()
 7995        },
 7996        Some(tree_sitter_rust::LANGUAGE.into()),
 7997    ));
 7998
 7999    cx.language_registry().add(language.clone());
 8000    cx.update_buffer(|buffer, cx| {
 8001        buffer.set_language(Some(language), cx);
 8002    });
 8003
 8004    cx.set_state(
 8005        &"
 8006            ˇ
 8007            ˇ
 8008            ˇ
 8009        "
 8010        .unindent(),
 8011    );
 8012
 8013    // ensure only matching closing brackets are skipped over
 8014    cx.update_editor(|editor, window, cx| {
 8015        editor.handle_input("}", window, cx);
 8016        editor.move_left(&MoveLeft, window, cx);
 8017        editor.handle_input(")", window, cx);
 8018        editor.move_left(&MoveLeft, window, cx);
 8019    });
 8020    cx.assert_editor_state(
 8021        &"
 8022            ˇ)}
 8023            ˇ)}
 8024            ˇ)}
 8025        "
 8026        .unindent(),
 8027    );
 8028
 8029    // skip-over closing brackets at multiple cursors
 8030    cx.update_editor(|editor, window, cx| {
 8031        editor.handle_input(")", window, cx);
 8032        editor.handle_input("}", window, cx);
 8033    });
 8034    cx.assert_editor_state(
 8035        &"
 8036            )}ˇ
 8037            )}ˇ
 8038            )}ˇ
 8039        "
 8040        .unindent(),
 8041    );
 8042
 8043    // ignore non-close brackets
 8044    cx.update_editor(|editor, window, cx| {
 8045        editor.handle_input("]", window, cx);
 8046        editor.move_left(&MoveLeft, window, cx);
 8047        editor.handle_input("]", window, cx);
 8048    });
 8049    cx.assert_editor_state(
 8050        &"
 8051            )}]ˇ]
 8052            )}]ˇ]
 8053            )}]ˇ]
 8054        "
 8055        .unindent(),
 8056    );
 8057}
 8058
 8059#[gpui::test]
 8060async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 8061    init_test(cx, |_| {});
 8062
 8063    let mut cx = EditorTestContext::new(cx).await;
 8064
 8065    let html_language = Arc::new(
 8066        Language::new(
 8067            LanguageConfig {
 8068                name: "HTML".into(),
 8069                brackets: BracketPairConfig {
 8070                    pairs: vec![
 8071                        BracketPair {
 8072                            start: "<".into(),
 8073                            end: ">".into(),
 8074                            close: true,
 8075                            ..Default::default()
 8076                        },
 8077                        BracketPair {
 8078                            start: "{".into(),
 8079                            end: "}".into(),
 8080                            close: true,
 8081                            ..Default::default()
 8082                        },
 8083                        BracketPair {
 8084                            start: "(".into(),
 8085                            end: ")".into(),
 8086                            close: true,
 8087                            ..Default::default()
 8088                        },
 8089                    ],
 8090                    ..Default::default()
 8091                },
 8092                autoclose_before: "})]>".into(),
 8093                ..Default::default()
 8094            },
 8095            Some(tree_sitter_html::LANGUAGE.into()),
 8096        )
 8097        .with_injection_query(
 8098            r#"
 8099            (script_element
 8100                (raw_text) @injection.content
 8101                (#set! injection.language "javascript"))
 8102            "#,
 8103        )
 8104        .unwrap(),
 8105    );
 8106
 8107    let javascript_language = Arc::new(Language::new(
 8108        LanguageConfig {
 8109            name: "JavaScript".into(),
 8110            brackets: BracketPairConfig {
 8111                pairs: vec![
 8112                    BracketPair {
 8113                        start: "/*".into(),
 8114                        end: " */".into(),
 8115                        close: true,
 8116                        ..Default::default()
 8117                    },
 8118                    BracketPair {
 8119                        start: "{".into(),
 8120                        end: "}".into(),
 8121                        close: true,
 8122                        ..Default::default()
 8123                    },
 8124                    BracketPair {
 8125                        start: "(".into(),
 8126                        end: ")".into(),
 8127                        close: true,
 8128                        ..Default::default()
 8129                    },
 8130                ],
 8131                ..Default::default()
 8132            },
 8133            autoclose_before: "})]>".into(),
 8134            ..Default::default()
 8135        },
 8136        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8137    ));
 8138
 8139    cx.language_registry().add(html_language.clone());
 8140    cx.language_registry().add(javascript_language.clone());
 8141
 8142    cx.update_buffer(|buffer, cx| {
 8143        buffer.set_language(Some(html_language), cx);
 8144    });
 8145
 8146    cx.set_state(
 8147        &r#"
 8148            <body>ˇ
 8149                <script>
 8150                    var x = 1;ˇ
 8151                </script>
 8152            </body>ˇ
 8153        "#
 8154        .unindent(),
 8155    );
 8156
 8157    // Precondition: different languages are active at different locations.
 8158    cx.update_editor(|editor, window, cx| {
 8159        let snapshot = editor.snapshot(window, cx);
 8160        let cursors = editor.selections.ranges::<usize>(cx);
 8161        let languages = cursors
 8162            .iter()
 8163            .map(|c| snapshot.language_at(c.start).unwrap().name())
 8164            .collect::<Vec<_>>();
 8165        assert_eq!(
 8166            languages,
 8167            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 8168        );
 8169    });
 8170
 8171    // Angle brackets autoclose in HTML, but not JavaScript.
 8172    cx.update_editor(|editor, window, cx| {
 8173        editor.handle_input("<", window, cx);
 8174        editor.handle_input("a", window, cx);
 8175    });
 8176    cx.assert_editor_state(
 8177        &r#"
 8178            <body><aˇ>
 8179                <script>
 8180                    var x = 1;<aˇ
 8181                </script>
 8182            </body><aˇ>
 8183        "#
 8184        .unindent(),
 8185    );
 8186
 8187    // Curly braces and parens autoclose in both HTML and JavaScript.
 8188    cx.update_editor(|editor, window, cx| {
 8189        editor.handle_input(" b=", window, cx);
 8190        editor.handle_input("{", window, cx);
 8191        editor.handle_input("c", window, cx);
 8192        editor.handle_input("(", window, cx);
 8193    });
 8194    cx.assert_editor_state(
 8195        &r#"
 8196            <body><a b={c(ˇ)}>
 8197                <script>
 8198                    var x = 1;<a b={c(ˇ)}
 8199                </script>
 8200            </body><a b={c(ˇ)}>
 8201        "#
 8202        .unindent(),
 8203    );
 8204
 8205    // Brackets that were already autoclosed are skipped.
 8206    cx.update_editor(|editor, window, cx| {
 8207        editor.handle_input(")", window, cx);
 8208        editor.handle_input("d", window, cx);
 8209        editor.handle_input("}", window, cx);
 8210    });
 8211    cx.assert_editor_state(
 8212        &r#"
 8213            <body><a b={c()d}ˇ>
 8214                <script>
 8215                    var x = 1;<a b={c()d}ˇ
 8216                </script>
 8217            </body><a b={c()d}ˇ>
 8218        "#
 8219        .unindent(),
 8220    );
 8221    cx.update_editor(|editor, window, cx| {
 8222        editor.handle_input(">", window, cx);
 8223    });
 8224    cx.assert_editor_state(
 8225        &r#"
 8226            <body><a b={c()d}>ˇ
 8227                <script>
 8228                    var x = 1;<a b={c()d}>ˇ
 8229                </script>
 8230            </body><a b={c()d}>ˇ
 8231        "#
 8232        .unindent(),
 8233    );
 8234
 8235    // Reset
 8236    cx.set_state(
 8237        &r#"
 8238            <body>ˇ
 8239                <script>
 8240                    var x = 1;ˇ
 8241                </script>
 8242            </body>ˇ
 8243        "#
 8244        .unindent(),
 8245    );
 8246
 8247    cx.update_editor(|editor, window, cx| {
 8248        editor.handle_input("<", window, cx);
 8249    });
 8250    cx.assert_editor_state(
 8251        &r#"
 8252            <body><ˇ>
 8253                <script>
 8254                    var x = 1;<ˇ
 8255                </script>
 8256            </body><ˇ>
 8257        "#
 8258        .unindent(),
 8259    );
 8260
 8261    // When backspacing, the closing angle brackets are removed.
 8262    cx.update_editor(|editor, window, cx| {
 8263        editor.backspace(&Backspace, window, cx);
 8264    });
 8265    cx.assert_editor_state(
 8266        &r#"
 8267            <body>ˇ
 8268                <script>
 8269                    var x = 1;ˇ
 8270                </script>
 8271            </body>ˇ
 8272        "#
 8273        .unindent(),
 8274    );
 8275
 8276    // Block comments autoclose in JavaScript, but not HTML.
 8277    cx.update_editor(|editor, window, cx| {
 8278        editor.handle_input("/", window, cx);
 8279        editor.handle_input("*", window, cx);
 8280    });
 8281    cx.assert_editor_state(
 8282        &r#"
 8283            <body>/*ˇ
 8284                <script>
 8285                    var x = 1;/*ˇ */
 8286                </script>
 8287            </body>/*ˇ
 8288        "#
 8289        .unindent(),
 8290    );
 8291}
 8292
 8293#[gpui::test]
 8294async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 8295    init_test(cx, |_| {});
 8296
 8297    let mut cx = EditorTestContext::new(cx).await;
 8298
 8299    let rust_language = Arc::new(
 8300        Language::new(
 8301            LanguageConfig {
 8302                name: "Rust".into(),
 8303                brackets: serde_json::from_value(json!([
 8304                    { "start": "{", "end": "}", "close": true, "newline": true },
 8305                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 8306                ]))
 8307                .unwrap(),
 8308                autoclose_before: "})]>".into(),
 8309                ..Default::default()
 8310            },
 8311            Some(tree_sitter_rust::LANGUAGE.into()),
 8312        )
 8313        .with_override_query("(string_literal) @string")
 8314        .unwrap(),
 8315    );
 8316
 8317    cx.language_registry().add(rust_language.clone());
 8318    cx.update_buffer(|buffer, cx| {
 8319        buffer.set_language(Some(rust_language), cx);
 8320    });
 8321
 8322    cx.set_state(
 8323        &r#"
 8324            let x = ˇ
 8325        "#
 8326        .unindent(),
 8327    );
 8328
 8329    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 8330    cx.update_editor(|editor, window, cx| {
 8331        editor.handle_input("\"", window, cx);
 8332    });
 8333    cx.assert_editor_state(
 8334        &r#"
 8335            let x = "ˇ"
 8336        "#
 8337        .unindent(),
 8338    );
 8339
 8340    // Inserting another quotation mark. The cursor moves across the existing
 8341    // automatically-inserted quotation mark.
 8342    cx.update_editor(|editor, window, cx| {
 8343        editor.handle_input("\"", window, cx);
 8344    });
 8345    cx.assert_editor_state(
 8346        &r#"
 8347            let x = ""ˇ
 8348        "#
 8349        .unindent(),
 8350    );
 8351
 8352    // Reset
 8353    cx.set_state(
 8354        &r#"
 8355            let x = ˇ
 8356        "#
 8357        .unindent(),
 8358    );
 8359
 8360    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 8361    cx.update_editor(|editor, window, cx| {
 8362        editor.handle_input("\"", window, cx);
 8363        editor.handle_input(" ", window, cx);
 8364        editor.move_left(&Default::default(), window, cx);
 8365        editor.handle_input("\\", window, cx);
 8366        editor.handle_input("\"", window, cx);
 8367    });
 8368    cx.assert_editor_state(
 8369        &r#"
 8370            let x = "\"ˇ "
 8371        "#
 8372        .unindent(),
 8373    );
 8374
 8375    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 8376    // mark. Nothing is inserted.
 8377    cx.update_editor(|editor, window, cx| {
 8378        editor.move_right(&Default::default(), window, cx);
 8379        editor.handle_input("\"", window, cx);
 8380    });
 8381    cx.assert_editor_state(
 8382        &r#"
 8383            let x = "\" "ˇ
 8384        "#
 8385        .unindent(),
 8386    );
 8387}
 8388
 8389#[gpui::test]
 8390async fn test_surround_with_pair(cx: &mut TestAppContext) {
 8391    init_test(cx, |_| {});
 8392
 8393    let language = Arc::new(Language::new(
 8394        LanguageConfig {
 8395            brackets: BracketPairConfig {
 8396                pairs: vec![
 8397                    BracketPair {
 8398                        start: "{".to_string(),
 8399                        end: "}".to_string(),
 8400                        close: true,
 8401                        surround: true,
 8402                        newline: true,
 8403                    },
 8404                    BracketPair {
 8405                        start: "/* ".to_string(),
 8406                        end: "*/".to_string(),
 8407                        close: true,
 8408                        surround: true,
 8409                        ..Default::default()
 8410                    },
 8411                ],
 8412                ..Default::default()
 8413            },
 8414            ..Default::default()
 8415        },
 8416        Some(tree_sitter_rust::LANGUAGE.into()),
 8417    ));
 8418
 8419    let text = r#"
 8420        a
 8421        b
 8422        c
 8423    "#
 8424    .unindent();
 8425
 8426    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8427    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8428    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8429    editor
 8430        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8431        .await;
 8432
 8433    editor.update_in(cx, |editor, window, cx| {
 8434        editor.change_selections(None, window, cx, |s| {
 8435            s.select_display_ranges([
 8436                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8437                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8438                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 8439            ])
 8440        });
 8441
 8442        editor.handle_input("{", window, cx);
 8443        editor.handle_input("{", window, cx);
 8444        editor.handle_input("{", window, cx);
 8445        assert_eq!(
 8446            editor.text(cx),
 8447            "
 8448                {{{a}}}
 8449                {{{b}}}
 8450                {{{c}}}
 8451            "
 8452            .unindent()
 8453        );
 8454        assert_eq!(
 8455            editor.selections.display_ranges(cx),
 8456            [
 8457                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 8458                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 8459                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 8460            ]
 8461        );
 8462
 8463        editor.undo(&Undo, window, cx);
 8464        editor.undo(&Undo, window, cx);
 8465        editor.undo(&Undo, window, cx);
 8466        assert_eq!(
 8467            editor.text(cx),
 8468            "
 8469                a
 8470                b
 8471                c
 8472            "
 8473            .unindent()
 8474        );
 8475        assert_eq!(
 8476            editor.selections.display_ranges(cx),
 8477            [
 8478                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8479                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8480                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8481            ]
 8482        );
 8483
 8484        // Ensure inserting the first character of a multi-byte bracket pair
 8485        // doesn't surround the selections with the bracket.
 8486        editor.handle_input("/", window, cx);
 8487        assert_eq!(
 8488            editor.text(cx),
 8489            "
 8490                /
 8491                /
 8492                /
 8493            "
 8494            .unindent()
 8495        );
 8496        assert_eq!(
 8497            editor.selections.display_ranges(cx),
 8498            [
 8499                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8500                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8501                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8502            ]
 8503        );
 8504
 8505        editor.undo(&Undo, window, cx);
 8506        assert_eq!(
 8507            editor.text(cx),
 8508            "
 8509                a
 8510                b
 8511                c
 8512            "
 8513            .unindent()
 8514        );
 8515        assert_eq!(
 8516            editor.selections.display_ranges(cx),
 8517            [
 8518                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8519                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8520                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8521            ]
 8522        );
 8523
 8524        // Ensure inserting the last character of a multi-byte bracket pair
 8525        // doesn't surround the selections with the bracket.
 8526        editor.handle_input("*", window, cx);
 8527        assert_eq!(
 8528            editor.text(cx),
 8529            "
 8530                *
 8531                *
 8532                *
 8533            "
 8534            .unindent()
 8535        );
 8536        assert_eq!(
 8537            editor.selections.display_ranges(cx),
 8538            [
 8539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8540                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8541                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8542            ]
 8543        );
 8544    });
 8545}
 8546
 8547#[gpui::test]
 8548async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8549    init_test(cx, |_| {});
 8550
 8551    let language = Arc::new(Language::new(
 8552        LanguageConfig {
 8553            brackets: BracketPairConfig {
 8554                pairs: vec![BracketPair {
 8555                    start: "{".to_string(),
 8556                    end: "}".to_string(),
 8557                    close: true,
 8558                    surround: true,
 8559                    newline: true,
 8560                }],
 8561                ..Default::default()
 8562            },
 8563            autoclose_before: "}".to_string(),
 8564            ..Default::default()
 8565        },
 8566        Some(tree_sitter_rust::LANGUAGE.into()),
 8567    ));
 8568
 8569    let text = r#"
 8570        a
 8571        b
 8572        c
 8573    "#
 8574    .unindent();
 8575
 8576    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8577    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8578    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8579    editor
 8580        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8581        .await;
 8582
 8583    editor.update_in(cx, |editor, window, cx| {
 8584        editor.change_selections(None, window, cx, |s| {
 8585            s.select_ranges([
 8586                Point::new(0, 1)..Point::new(0, 1),
 8587                Point::new(1, 1)..Point::new(1, 1),
 8588                Point::new(2, 1)..Point::new(2, 1),
 8589            ])
 8590        });
 8591
 8592        editor.handle_input("{", window, cx);
 8593        editor.handle_input("{", window, cx);
 8594        editor.handle_input("_", window, cx);
 8595        assert_eq!(
 8596            editor.text(cx),
 8597            "
 8598                a{{_}}
 8599                b{{_}}
 8600                c{{_}}
 8601            "
 8602            .unindent()
 8603        );
 8604        assert_eq!(
 8605            editor.selections.ranges::<Point>(cx),
 8606            [
 8607                Point::new(0, 4)..Point::new(0, 4),
 8608                Point::new(1, 4)..Point::new(1, 4),
 8609                Point::new(2, 4)..Point::new(2, 4)
 8610            ]
 8611        );
 8612
 8613        editor.backspace(&Default::default(), window, cx);
 8614        editor.backspace(&Default::default(), window, cx);
 8615        assert_eq!(
 8616            editor.text(cx),
 8617            "
 8618                a{}
 8619                b{}
 8620                c{}
 8621            "
 8622            .unindent()
 8623        );
 8624        assert_eq!(
 8625            editor.selections.ranges::<Point>(cx),
 8626            [
 8627                Point::new(0, 2)..Point::new(0, 2),
 8628                Point::new(1, 2)..Point::new(1, 2),
 8629                Point::new(2, 2)..Point::new(2, 2)
 8630            ]
 8631        );
 8632
 8633        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8634        assert_eq!(
 8635            editor.text(cx),
 8636            "
 8637                a
 8638                b
 8639                c
 8640            "
 8641            .unindent()
 8642        );
 8643        assert_eq!(
 8644            editor.selections.ranges::<Point>(cx),
 8645            [
 8646                Point::new(0, 1)..Point::new(0, 1),
 8647                Point::new(1, 1)..Point::new(1, 1),
 8648                Point::new(2, 1)..Point::new(2, 1)
 8649            ]
 8650        );
 8651    });
 8652}
 8653
 8654#[gpui::test]
 8655async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8656    init_test(cx, |settings| {
 8657        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8658    });
 8659
 8660    let mut cx = EditorTestContext::new(cx).await;
 8661
 8662    let language = Arc::new(Language::new(
 8663        LanguageConfig {
 8664            brackets: BracketPairConfig {
 8665                pairs: vec![
 8666                    BracketPair {
 8667                        start: "{".to_string(),
 8668                        end: "}".to_string(),
 8669                        close: true,
 8670                        surround: true,
 8671                        newline: true,
 8672                    },
 8673                    BracketPair {
 8674                        start: "(".to_string(),
 8675                        end: ")".to_string(),
 8676                        close: true,
 8677                        surround: true,
 8678                        newline: true,
 8679                    },
 8680                    BracketPair {
 8681                        start: "[".to_string(),
 8682                        end: "]".to_string(),
 8683                        close: false,
 8684                        surround: true,
 8685                        newline: true,
 8686                    },
 8687                ],
 8688                ..Default::default()
 8689            },
 8690            autoclose_before: "})]".to_string(),
 8691            ..Default::default()
 8692        },
 8693        Some(tree_sitter_rust::LANGUAGE.into()),
 8694    ));
 8695
 8696    cx.language_registry().add(language.clone());
 8697    cx.update_buffer(|buffer, cx| {
 8698        buffer.set_language(Some(language), cx);
 8699    });
 8700
 8701    cx.set_state(
 8702        &"
 8703            {(ˇ)}
 8704            [[ˇ]]
 8705            {(ˇ)}
 8706        "
 8707        .unindent(),
 8708    );
 8709
 8710    cx.update_editor(|editor, window, cx| {
 8711        editor.backspace(&Default::default(), window, cx);
 8712        editor.backspace(&Default::default(), window, cx);
 8713    });
 8714
 8715    cx.assert_editor_state(
 8716        &"
 8717            ˇ
 8718            ˇ]]
 8719            ˇ
 8720        "
 8721        .unindent(),
 8722    );
 8723
 8724    cx.update_editor(|editor, window, cx| {
 8725        editor.handle_input("{", window, cx);
 8726        editor.handle_input("{", window, cx);
 8727        editor.move_right(&MoveRight, window, cx);
 8728        editor.move_right(&MoveRight, window, cx);
 8729        editor.move_left(&MoveLeft, window, cx);
 8730        editor.move_left(&MoveLeft, window, cx);
 8731        editor.backspace(&Default::default(), window, cx);
 8732    });
 8733
 8734    cx.assert_editor_state(
 8735        &"
 8736            {ˇ}
 8737            {ˇ}]]
 8738            {ˇ}
 8739        "
 8740        .unindent(),
 8741    );
 8742
 8743    cx.update_editor(|editor, window, cx| {
 8744        editor.backspace(&Default::default(), window, cx);
 8745    });
 8746
 8747    cx.assert_editor_state(
 8748        &"
 8749            ˇ
 8750            ˇ]]
 8751            ˇ
 8752        "
 8753        .unindent(),
 8754    );
 8755}
 8756
 8757#[gpui::test]
 8758async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8759    init_test(cx, |_| {});
 8760
 8761    let language = Arc::new(Language::new(
 8762        LanguageConfig::default(),
 8763        Some(tree_sitter_rust::LANGUAGE.into()),
 8764    ));
 8765
 8766    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8767    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8768    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8769    editor
 8770        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8771        .await;
 8772
 8773    editor.update_in(cx, |editor, window, cx| {
 8774        editor.set_auto_replace_emoji_shortcode(true);
 8775
 8776        editor.handle_input("Hello ", window, cx);
 8777        editor.handle_input(":wave", window, cx);
 8778        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8779
 8780        editor.handle_input(":", window, cx);
 8781        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8782
 8783        editor.handle_input(" :smile", window, cx);
 8784        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8785
 8786        editor.handle_input(":", window, cx);
 8787        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8788
 8789        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8790        editor.handle_input(":wave", window, cx);
 8791        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8792
 8793        editor.handle_input(":", window, cx);
 8794        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8795
 8796        editor.handle_input(":1", window, cx);
 8797        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8798
 8799        editor.handle_input(":", window, cx);
 8800        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8801
 8802        // Ensure shortcode does not get replaced when it is part of a word
 8803        editor.handle_input(" Test:wave", window, cx);
 8804        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8805
 8806        editor.handle_input(":", window, cx);
 8807        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8808
 8809        editor.set_auto_replace_emoji_shortcode(false);
 8810
 8811        // Ensure shortcode does not get replaced when auto replace is off
 8812        editor.handle_input(" :wave", window, cx);
 8813        assert_eq!(
 8814            editor.text(cx),
 8815            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8816        );
 8817
 8818        editor.handle_input(":", window, cx);
 8819        assert_eq!(
 8820            editor.text(cx),
 8821            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8822        );
 8823    });
 8824}
 8825
 8826#[gpui::test]
 8827async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8828    init_test(cx, |_| {});
 8829
 8830    let (text, insertion_ranges) = marked_text_ranges(
 8831        indoc! {"
 8832            ˇ
 8833        "},
 8834        false,
 8835    );
 8836
 8837    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8838    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8839
 8840    _ = editor.update_in(cx, |editor, window, cx| {
 8841        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8842
 8843        editor
 8844            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8845            .unwrap();
 8846
 8847        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8848            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8849            assert_eq!(editor.text(cx), expected_text);
 8850            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8851        }
 8852
 8853        assert(
 8854            editor,
 8855            cx,
 8856            indoc! {"
 8857            type «» =•
 8858            "},
 8859        );
 8860
 8861        assert!(editor.context_menu_visible(), "There should be a matches");
 8862    });
 8863}
 8864
 8865#[gpui::test]
 8866async fn test_snippets(cx: &mut TestAppContext) {
 8867    init_test(cx, |_| {});
 8868
 8869    let mut cx = EditorTestContext::new(cx).await;
 8870
 8871    cx.set_state(indoc! {"
 8872        a.ˇ b
 8873        a.ˇ b
 8874        a.ˇ b
 8875    "});
 8876
 8877    cx.update_editor(|editor, window, cx| {
 8878        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8879        let insertion_ranges = editor
 8880            .selections
 8881            .all(cx)
 8882            .iter()
 8883            .map(|s| s.range().clone())
 8884            .collect::<Vec<_>>();
 8885        editor
 8886            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8887            .unwrap();
 8888    });
 8889
 8890    cx.assert_editor_state(indoc! {"
 8891        a.f(«oneˇ», two, «threeˇ») b
 8892        a.f(«oneˇ», two, «threeˇ») b
 8893        a.f(«oneˇ», two, «threeˇ») b
 8894    "});
 8895
 8896    // Can't move earlier than the first tab stop
 8897    cx.update_editor(|editor, window, cx| {
 8898        assert!(!editor.move_to_prev_snippet_tabstop(window, cx))
 8899    });
 8900    cx.assert_editor_state(indoc! {"
 8901        a.f(«oneˇ», two, «threeˇ») b
 8902        a.f(«oneˇ», two, «threeˇ») b
 8903        a.f(«oneˇ», two, «threeˇ») b
 8904    "});
 8905
 8906    cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
 8907    cx.assert_editor_state(indoc! {"
 8908        a.f(one, «twoˇ», three) b
 8909        a.f(one, «twoˇ», three) b
 8910        a.f(one, «twoˇ», three) b
 8911    "});
 8912
 8913    cx.update_editor(|editor, window, cx| assert!(editor.move_to_prev_snippet_tabstop(window, cx)));
 8914    cx.assert_editor_state(indoc! {"
 8915        a.f(«oneˇ», two, «threeˇ») b
 8916        a.f(«oneˇ», two, «threeˇ») b
 8917        a.f(«oneˇ», two, «threeˇ») b
 8918    "});
 8919
 8920    cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
 8921    cx.assert_editor_state(indoc! {"
 8922        a.f(one, «twoˇ», three) b
 8923        a.f(one, «twoˇ», three) b
 8924        a.f(one, «twoˇ», three) b
 8925    "});
 8926    cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
 8927    cx.assert_editor_state(indoc! {"
 8928        a.f(one, two, three)ˇ b
 8929        a.f(one, two, three)ˇ b
 8930        a.f(one, two, three)ˇ b
 8931    "});
 8932
 8933    // As soon as the last tab stop is reached, snippet state is gone
 8934    cx.update_editor(|editor, window, cx| {
 8935        assert!(!editor.move_to_prev_snippet_tabstop(window, cx))
 8936    });
 8937    cx.assert_editor_state(indoc! {"
 8938        a.f(one, two, three)ˇ b
 8939        a.f(one, two, three)ˇ b
 8940        a.f(one, two, three)ˇ b
 8941    "});
 8942}
 8943
 8944#[gpui::test]
 8945async fn test_snippet_indentation(cx: &mut TestAppContext) {
 8946    init_test(cx, |_| {});
 8947
 8948    let mut cx = EditorTestContext::new(cx).await;
 8949
 8950    cx.update_editor(|editor, window, cx| {
 8951        let snippet = Snippet::parse(indoc! {"
 8952            /*
 8953             * Multiline comment with leading indentation
 8954             *
 8955             * $1
 8956             */
 8957            $0"})
 8958        .unwrap();
 8959        let insertion_ranges = editor
 8960            .selections
 8961            .all(cx)
 8962            .iter()
 8963            .map(|s| s.range().clone())
 8964            .collect::<Vec<_>>();
 8965        editor
 8966            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8967            .unwrap();
 8968    });
 8969
 8970    cx.assert_editor_state(indoc! {"
 8971        /*
 8972         * Multiline comment with leading indentation
 8973         *
 8974         * ˇ
 8975         */
 8976    "});
 8977
 8978    cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
 8979    cx.assert_editor_state(indoc! {"
 8980        /*
 8981         * Multiline comment with leading indentation
 8982         *
 8983         *•
 8984         */
 8985        ˇ"});
 8986}
 8987
 8988#[gpui::test]
 8989async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8990    init_test(cx, |_| {});
 8991
 8992    let fs = FakeFs::new(cx.executor());
 8993    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8994
 8995    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8996
 8997    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8998    language_registry.add(rust_lang());
 8999    let mut fake_servers = language_registry.register_fake_lsp(
 9000        "Rust",
 9001        FakeLspAdapter {
 9002            capabilities: lsp::ServerCapabilities {
 9003                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9004                ..Default::default()
 9005            },
 9006            ..Default::default()
 9007        },
 9008    );
 9009
 9010    let buffer = project
 9011        .update(cx, |project, cx| {
 9012            project.open_local_buffer(path!("/file.rs"), cx)
 9013        })
 9014        .await
 9015        .unwrap();
 9016
 9017    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9018    let (editor, cx) = cx.add_window_view(|window, cx| {
 9019        build_editor_with_project(project.clone(), buffer, window, cx)
 9020    });
 9021    editor.update_in(cx, |editor, window, cx| {
 9022        editor.set_text("one\ntwo\nthree\n", window, cx)
 9023    });
 9024    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9025
 9026    cx.executor().start_waiting();
 9027    let fake_server = fake_servers.next().await.unwrap();
 9028
 9029    {
 9030        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9031            move |params, _| async move {
 9032                assert_eq!(
 9033                    params.text_document.uri,
 9034                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9035                );
 9036                assert_eq!(params.options.tab_size, 4);
 9037                Ok(Some(vec![lsp::TextEdit::new(
 9038                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9039                    ", ".to_string(),
 9040                )]))
 9041            },
 9042        );
 9043        let save = editor
 9044            .update_in(cx, |editor, window, cx| {
 9045                editor.save(true, project.clone(), window, cx)
 9046            })
 9047            .unwrap();
 9048        cx.executor().start_waiting();
 9049        save.await;
 9050
 9051        assert_eq!(
 9052            editor.update(cx, |editor, cx| editor.text(cx)),
 9053            "one, two\nthree\n"
 9054        );
 9055        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9056    }
 9057
 9058    {
 9059        editor.update_in(cx, |editor, window, cx| {
 9060            editor.set_text("one\ntwo\nthree\n", window, cx)
 9061        });
 9062        assert!(cx.read(|cx| editor.is_dirty(cx)));
 9063
 9064        // Ensure we can still save even if formatting hangs.
 9065        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9066            move |params, _| async move {
 9067                assert_eq!(
 9068                    params.text_document.uri,
 9069                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9070                );
 9071                futures::future::pending::<()>().await;
 9072                unreachable!()
 9073            },
 9074        );
 9075        let save = editor
 9076            .update_in(cx, |editor, window, cx| {
 9077                editor.save(true, project.clone(), window, cx)
 9078            })
 9079            .unwrap();
 9080        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9081        cx.executor().start_waiting();
 9082        save.await;
 9083        assert_eq!(
 9084            editor.update(cx, |editor, cx| editor.text(cx)),
 9085            "one\ntwo\nthree\n"
 9086        );
 9087    }
 9088
 9089    // For non-dirty buffer, no formatting request should be sent
 9090    {
 9091        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9092
 9093        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 9094            panic!("Should not be invoked on non-dirty buffer");
 9095        });
 9096        let save = editor
 9097            .update_in(cx, |editor, window, cx| {
 9098                editor.save(true, project.clone(), window, cx)
 9099            })
 9100            .unwrap();
 9101        cx.executor().start_waiting();
 9102        save.await;
 9103    }
 9104
 9105    // Set rust language override and assert overridden tabsize is sent to language server
 9106    update_test_language_settings(cx, |settings| {
 9107        settings.languages.insert(
 9108            "Rust".into(),
 9109            LanguageSettingsContent {
 9110                tab_size: NonZeroU32::new(8),
 9111                ..Default::default()
 9112            },
 9113        );
 9114    });
 9115
 9116    {
 9117        editor.update_in(cx, |editor, window, cx| {
 9118            editor.set_text("somehting_new\n", window, cx)
 9119        });
 9120        assert!(cx.read(|cx| editor.is_dirty(cx)));
 9121        let _formatting_request_signal = fake_server
 9122            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9123                assert_eq!(
 9124                    params.text_document.uri,
 9125                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9126                );
 9127                assert_eq!(params.options.tab_size, 8);
 9128                Ok(Some(vec![]))
 9129            });
 9130        let save = editor
 9131            .update_in(cx, |editor, window, cx| {
 9132                editor.save(true, project.clone(), window, cx)
 9133            })
 9134            .unwrap();
 9135        cx.executor().start_waiting();
 9136        save.await;
 9137    }
 9138}
 9139
 9140#[gpui::test]
 9141async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 9142    init_test(cx, |_| {});
 9143
 9144    let cols = 4;
 9145    let rows = 10;
 9146    let sample_text_1 = sample_text(rows, cols, 'a');
 9147    assert_eq!(
 9148        sample_text_1,
 9149        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9150    );
 9151    let sample_text_2 = sample_text(rows, cols, 'l');
 9152    assert_eq!(
 9153        sample_text_2,
 9154        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9155    );
 9156    let sample_text_3 = sample_text(rows, cols, 'v');
 9157    assert_eq!(
 9158        sample_text_3,
 9159        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9160    );
 9161
 9162    let fs = FakeFs::new(cx.executor());
 9163    fs.insert_tree(
 9164        path!("/a"),
 9165        json!({
 9166            "main.rs": sample_text_1,
 9167            "other.rs": sample_text_2,
 9168            "lib.rs": sample_text_3,
 9169        }),
 9170    )
 9171    .await;
 9172
 9173    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9174    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9175    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9176
 9177    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9178    language_registry.add(rust_lang());
 9179    let mut fake_servers = language_registry.register_fake_lsp(
 9180        "Rust",
 9181        FakeLspAdapter {
 9182            capabilities: lsp::ServerCapabilities {
 9183                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9184                ..Default::default()
 9185            },
 9186            ..Default::default()
 9187        },
 9188    );
 9189
 9190    let worktree = project.update(cx, |project, cx| {
 9191        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 9192        assert_eq!(worktrees.len(), 1);
 9193        worktrees.pop().unwrap()
 9194    });
 9195    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 9196
 9197    let buffer_1 = project
 9198        .update(cx, |project, cx| {
 9199            project.open_buffer((worktree_id, "main.rs"), cx)
 9200        })
 9201        .await
 9202        .unwrap();
 9203    let buffer_2 = project
 9204        .update(cx, |project, cx| {
 9205            project.open_buffer((worktree_id, "other.rs"), cx)
 9206        })
 9207        .await
 9208        .unwrap();
 9209    let buffer_3 = project
 9210        .update(cx, |project, cx| {
 9211            project.open_buffer((worktree_id, "lib.rs"), cx)
 9212        })
 9213        .await
 9214        .unwrap();
 9215
 9216    let multi_buffer = cx.new(|cx| {
 9217        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 9218        multi_buffer.push_excerpts(
 9219            buffer_1.clone(),
 9220            [
 9221                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 9222                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 9223                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 9224            ],
 9225            cx,
 9226        );
 9227        multi_buffer.push_excerpts(
 9228            buffer_2.clone(),
 9229            [
 9230                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 9231                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 9232                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 9233            ],
 9234            cx,
 9235        );
 9236        multi_buffer.push_excerpts(
 9237            buffer_3.clone(),
 9238            [
 9239                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 9240                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 9241                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 9242            ],
 9243            cx,
 9244        );
 9245        multi_buffer
 9246    });
 9247    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 9248        Editor::new(
 9249            EditorMode::full(),
 9250            multi_buffer,
 9251            Some(project.clone()),
 9252            window,
 9253            cx,
 9254        )
 9255    });
 9256
 9257    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 9258        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 9259            s.select_ranges(Some(1..2))
 9260        });
 9261        editor.insert("|one|two|three|", window, cx);
 9262    });
 9263    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 9264    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 9265        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 9266            s.select_ranges(Some(60..70))
 9267        });
 9268        editor.insert("|four|five|six|", window, cx);
 9269    });
 9270    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 9271
 9272    // First two buffers should be edited, but not the third one.
 9273    assert_eq!(
 9274        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 9275        "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}",
 9276    );
 9277    buffer_1.update(cx, |buffer, _| {
 9278        assert!(buffer.is_dirty());
 9279        assert_eq!(
 9280            buffer.text(),
 9281            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 9282        )
 9283    });
 9284    buffer_2.update(cx, |buffer, _| {
 9285        assert!(buffer.is_dirty());
 9286        assert_eq!(
 9287            buffer.text(),
 9288            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 9289        )
 9290    });
 9291    buffer_3.update(cx, |buffer, _| {
 9292        assert!(!buffer.is_dirty());
 9293        assert_eq!(buffer.text(), sample_text_3,)
 9294    });
 9295    cx.executor().run_until_parked();
 9296
 9297    cx.executor().start_waiting();
 9298    let save = multi_buffer_editor
 9299        .update_in(cx, |editor, window, cx| {
 9300            editor.save(true, project.clone(), window, cx)
 9301        })
 9302        .unwrap();
 9303
 9304    let fake_server = fake_servers.next().await.unwrap();
 9305    fake_server
 9306        .server
 9307        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9308            Ok(Some(vec![lsp::TextEdit::new(
 9309                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9310                format!("[{} formatted]", params.text_document.uri),
 9311            )]))
 9312        })
 9313        .detach();
 9314    save.await;
 9315
 9316    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 9317    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 9318    assert_eq!(
 9319        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 9320        uri!(
 9321            "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}"
 9322        ),
 9323    );
 9324    buffer_1.update(cx, |buffer, _| {
 9325        assert!(!buffer.is_dirty());
 9326        assert_eq!(
 9327            buffer.text(),
 9328            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 9329        )
 9330    });
 9331    buffer_2.update(cx, |buffer, _| {
 9332        assert!(!buffer.is_dirty());
 9333        assert_eq!(
 9334            buffer.text(),
 9335            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 9336        )
 9337    });
 9338    buffer_3.update(cx, |buffer, _| {
 9339        assert!(!buffer.is_dirty());
 9340        assert_eq!(buffer.text(), sample_text_3,)
 9341    });
 9342}
 9343
 9344#[gpui::test]
 9345async fn test_range_format_during_save(cx: &mut TestAppContext) {
 9346    init_test(cx, |_| {});
 9347
 9348    let fs = FakeFs::new(cx.executor());
 9349    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9350
 9351    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9352
 9353    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9354    language_registry.add(rust_lang());
 9355    let mut fake_servers = language_registry.register_fake_lsp(
 9356        "Rust",
 9357        FakeLspAdapter {
 9358            capabilities: lsp::ServerCapabilities {
 9359                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 9360                ..Default::default()
 9361            },
 9362            ..Default::default()
 9363        },
 9364    );
 9365
 9366    let buffer = project
 9367        .update(cx, |project, cx| {
 9368            project.open_local_buffer(path!("/file.rs"), cx)
 9369        })
 9370        .await
 9371        .unwrap();
 9372
 9373    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9374    let (editor, cx) = cx.add_window_view(|window, cx| {
 9375        build_editor_with_project(project.clone(), buffer, window, cx)
 9376    });
 9377    editor.update_in(cx, |editor, window, cx| {
 9378        editor.set_text("one\ntwo\nthree\n", window, cx)
 9379    });
 9380    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9381
 9382    cx.executor().start_waiting();
 9383    let fake_server = fake_servers.next().await.unwrap();
 9384
 9385    let save = editor
 9386        .update_in(cx, |editor, window, cx| {
 9387            editor.save(true, project.clone(), window, cx)
 9388        })
 9389        .unwrap();
 9390    fake_server
 9391        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9392            assert_eq!(
 9393                params.text_document.uri,
 9394                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9395            );
 9396            assert_eq!(params.options.tab_size, 4);
 9397            Ok(Some(vec![lsp::TextEdit::new(
 9398                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9399                ", ".to_string(),
 9400            )]))
 9401        })
 9402        .next()
 9403        .await;
 9404    cx.executor().start_waiting();
 9405    save.await;
 9406    assert_eq!(
 9407        editor.update(cx, |editor, cx| editor.text(cx)),
 9408        "one, two\nthree\n"
 9409    );
 9410    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9411
 9412    editor.update_in(cx, |editor, window, cx| {
 9413        editor.set_text("one\ntwo\nthree\n", window, cx)
 9414    });
 9415    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9416
 9417    // Ensure we can still save even if formatting hangs.
 9418    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 9419        move |params, _| async move {
 9420            assert_eq!(
 9421                params.text_document.uri,
 9422                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9423            );
 9424            futures::future::pending::<()>().await;
 9425            unreachable!()
 9426        },
 9427    );
 9428    let save = editor
 9429        .update_in(cx, |editor, window, cx| {
 9430            editor.save(true, project.clone(), window, cx)
 9431        })
 9432        .unwrap();
 9433    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9434    cx.executor().start_waiting();
 9435    save.await;
 9436    assert_eq!(
 9437        editor.update(cx, |editor, cx| editor.text(cx)),
 9438        "one\ntwo\nthree\n"
 9439    );
 9440    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9441
 9442    // For non-dirty buffer, no formatting request should be sent
 9443    let save = editor
 9444        .update_in(cx, |editor, window, cx| {
 9445            editor.save(true, project.clone(), window, cx)
 9446        })
 9447        .unwrap();
 9448    let _pending_format_request = fake_server
 9449        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 9450            panic!("Should not be invoked on non-dirty buffer");
 9451        })
 9452        .next();
 9453    cx.executor().start_waiting();
 9454    save.await;
 9455
 9456    // Set Rust language override and assert overridden tabsize is sent to language server
 9457    update_test_language_settings(cx, |settings| {
 9458        settings.languages.insert(
 9459            "Rust".into(),
 9460            LanguageSettingsContent {
 9461                tab_size: NonZeroU32::new(8),
 9462                ..Default::default()
 9463            },
 9464        );
 9465    });
 9466
 9467    editor.update_in(cx, |editor, window, cx| {
 9468        editor.set_text("somehting_new\n", window, cx)
 9469    });
 9470    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9471    let save = editor
 9472        .update_in(cx, |editor, window, cx| {
 9473            editor.save(true, project.clone(), window, cx)
 9474        })
 9475        .unwrap();
 9476    fake_server
 9477        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9478            assert_eq!(
 9479                params.text_document.uri,
 9480                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9481            );
 9482            assert_eq!(params.options.tab_size, 8);
 9483            Ok(Some(Vec::new()))
 9484        })
 9485        .next()
 9486        .await;
 9487    save.await;
 9488}
 9489
 9490#[gpui::test]
 9491async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 9492    init_test(cx, |settings| {
 9493        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9494            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9495        ))
 9496    });
 9497
 9498    let fs = FakeFs::new(cx.executor());
 9499    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9500
 9501    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9502
 9503    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9504    language_registry.add(Arc::new(Language::new(
 9505        LanguageConfig {
 9506            name: "Rust".into(),
 9507            matcher: LanguageMatcher {
 9508                path_suffixes: vec!["rs".to_string()],
 9509                ..Default::default()
 9510            },
 9511            ..LanguageConfig::default()
 9512        },
 9513        Some(tree_sitter_rust::LANGUAGE.into()),
 9514    )));
 9515    update_test_language_settings(cx, |settings| {
 9516        // Enable Prettier formatting for the same buffer, and ensure
 9517        // LSP is called instead of Prettier.
 9518        settings.defaults.prettier = Some(PrettierSettings {
 9519            allowed: true,
 9520            ..PrettierSettings::default()
 9521        });
 9522    });
 9523    let mut fake_servers = language_registry.register_fake_lsp(
 9524        "Rust",
 9525        FakeLspAdapter {
 9526            capabilities: lsp::ServerCapabilities {
 9527                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9528                ..Default::default()
 9529            },
 9530            ..Default::default()
 9531        },
 9532    );
 9533
 9534    let buffer = project
 9535        .update(cx, |project, cx| {
 9536            project.open_local_buffer(path!("/file.rs"), cx)
 9537        })
 9538        .await
 9539        .unwrap();
 9540
 9541    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9542    let (editor, cx) = cx.add_window_view(|window, cx| {
 9543        build_editor_with_project(project.clone(), buffer, window, cx)
 9544    });
 9545    editor.update_in(cx, |editor, window, cx| {
 9546        editor.set_text("one\ntwo\nthree\n", window, cx)
 9547    });
 9548
 9549    cx.executor().start_waiting();
 9550    let fake_server = fake_servers.next().await.unwrap();
 9551
 9552    let format = editor
 9553        .update_in(cx, |editor, window, cx| {
 9554            editor.perform_format(
 9555                project.clone(),
 9556                FormatTrigger::Manual,
 9557                FormatTarget::Buffers,
 9558                window,
 9559                cx,
 9560            )
 9561        })
 9562        .unwrap();
 9563    fake_server
 9564        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9565            assert_eq!(
 9566                params.text_document.uri,
 9567                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9568            );
 9569            assert_eq!(params.options.tab_size, 4);
 9570            Ok(Some(vec![lsp::TextEdit::new(
 9571                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9572                ", ".to_string(),
 9573            )]))
 9574        })
 9575        .next()
 9576        .await;
 9577    cx.executor().start_waiting();
 9578    format.await;
 9579    assert_eq!(
 9580        editor.update(cx, |editor, cx| editor.text(cx)),
 9581        "one, two\nthree\n"
 9582    );
 9583
 9584    editor.update_in(cx, |editor, window, cx| {
 9585        editor.set_text("one\ntwo\nthree\n", window, cx)
 9586    });
 9587    // Ensure we don't lock if formatting hangs.
 9588    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9589        move |params, _| async move {
 9590            assert_eq!(
 9591                params.text_document.uri,
 9592                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9593            );
 9594            futures::future::pending::<()>().await;
 9595            unreachable!()
 9596        },
 9597    );
 9598    let format = editor
 9599        .update_in(cx, |editor, window, cx| {
 9600            editor.perform_format(
 9601                project,
 9602                FormatTrigger::Manual,
 9603                FormatTarget::Buffers,
 9604                window,
 9605                cx,
 9606            )
 9607        })
 9608        .unwrap();
 9609    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9610    cx.executor().start_waiting();
 9611    format.await;
 9612    assert_eq!(
 9613        editor.update(cx, |editor, cx| editor.text(cx)),
 9614        "one\ntwo\nthree\n"
 9615    );
 9616}
 9617
 9618#[gpui::test]
 9619async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9620    init_test(cx, |settings| {
 9621        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9622        settings.defaults.formatter =
 9623            Some(language_settings::SelectedFormatter::List(FormatterList(
 9624                vec![
 9625                    Formatter::LanguageServer { name: None },
 9626                    Formatter::CodeActions(
 9627                        [
 9628                            ("code-action-1".into(), true),
 9629                            ("code-action-2".into(), true),
 9630                        ]
 9631                        .into_iter()
 9632                        .collect(),
 9633                    ),
 9634                ]
 9635                .into(),
 9636            )))
 9637    });
 9638
 9639    let fs = FakeFs::new(cx.executor());
 9640    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9641        .await;
 9642
 9643    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9644    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9645    language_registry.add(rust_lang());
 9646
 9647    let mut fake_servers = language_registry.register_fake_lsp(
 9648        "Rust",
 9649        FakeLspAdapter {
 9650            capabilities: lsp::ServerCapabilities {
 9651                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9652                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9653                    commands: vec!["the-command-for-code-action-1".into()],
 9654                    ..Default::default()
 9655                }),
 9656                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9657                ..Default::default()
 9658            },
 9659            ..Default::default()
 9660        },
 9661    );
 9662
 9663    let buffer = project
 9664        .update(cx, |project, cx| {
 9665            project.open_local_buffer(path!("/file.rs"), cx)
 9666        })
 9667        .await
 9668        .unwrap();
 9669
 9670    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9671    let (editor, cx) = cx.add_window_view(|window, cx| {
 9672        build_editor_with_project(project.clone(), buffer, window, cx)
 9673    });
 9674
 9675    cx.executor().start_waiting();
 9676
 9677    let fake_server = fake_servers.next().await.unwrap();
 9678    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9679        move |_params, _| async move {
 9680            Ok(Some(vec![lsp::TextEdit::new(
 9681                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9682                "applied-formatting\n".to_string(),
 9683            )]))
 9684        },
 9685    );
 9686    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9687        move |params, _| async move {
 9688            assert_eq!(
 9689                params.context.only,
 9690                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9691            );
 9692            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9693            Ok(Some(vec![
 9694                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9695                    kind: Some("code-action-1".into()),
 9696                    edit: Some(lsp::WorkspaceEdit::new(
 9697                        [(
 9698                            uri.clone(),
 9699                            vec![lsp::TextEdit::new(
 9700                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9701                                "applied-code-action-1-edit\n".to_string(),
 9702                            )],
 9703                        )]
 9704                        .into_iter()
 9705                        .collect(),
 9706                    )),
 9707                    command: Some(lsp::Command {
 9708                        command: "the-command-for-code-action-1".into(),
 9709                        ..Default::default()
 9710                    }),
 9711                    ..Default::default()
 9712                }),
 9713                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9714                    kind: Some("code-action-2".into()),
 9715                    edit: Some(lsp::WorkspaceEdit::new(
 9716                        [(
 9717                            uri.clone(),
 9718                            vec![lsp::TextEdit::new(
 9719                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9720                                "applied-code-action-2-edit\n".to_string(),
 9721                            )],
 9722                        )]
 9723                        .into_iter()
 9724                        .collect(),
 9725                    )),
 9726                    ..Default::default()
 9727                }),
 9728            ]))
 9729        },
 9730    );
 9731
 9732    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9733        move |params, _| async move { Ok(params) }
 9734    });
 9735
 9736    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9737    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9738        let fake = fake_server.clone();
 9739        let lock = command_lock.clone();
 9740        move |params, _| {
 9741            assert_eq!(params.command, "the-command-for-code-action-1");
 9742            let fake = fake.clone();
 9743            let lock = lock.clone();
 9744            async move {
 9745                lock.lock().await;
 9746                fake.server
 9747                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9748                        label: None,
 9749                        edit: lsp::WorkspaceEdit {
 9750                            changes: Some(
 9751                                [(
 9752                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9753                                    vec![lsp::TextEdit {
 9754                                        range: lsp::Range::new(
 9755                                            lsp::Position::new(0, 0),
 9756                                            lsp::Position::new(0, 0),
 9757                                        ),
 9758                                        new_text: "applied-code-action-1-command\n".into(),
 9759                                    }],
 9760                                )]
 9761                                .into_iter()
 9762                                .collect(),
 9763                            ),
 9764                            ..Default::default()
 9765                        },
 9766                    })
 9767                    .await
 9768                    .into_response()
 9769                    .unwrap();
 9770                Ok(Some(json!(null)))
 9771            }
 9772        }
 9773    });
 9774
 9775    cx.executor().start_waiting();
 9776    editor
 9777        .update_in(cx, |editor, window, cx| {
 9778            editor.perform_format(
 9779                project.clone(),
 9780                FormatTrigger::Manual,
 9781                FormatTarget::Buffers,
 9782                window,
 9783                cx,
 9784            )
 9785        })
 9786        .unwrap()
 9787        .await;
 9788    editor.update(cx, |editor, cx| {
 9789        assert_eq!(
 9790            editor.text(cx),
 9791            r#"
 9792                applied-code-action-2-edit
 9793                applied-code-action-1-command
 9794                applied-code-action-1-edit
 9795                applied-formatting
 9796                one
 9797                two
 9798                three
 9799            "#
 9800            .unindent()
 9801        );
 9802    });
 9803
 9804    editor.update_in(cx, |editor, window, cx| {
 9805        editor.undo(&Default::default(), window, cx);
 9806        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9807    });
 9808
 9809    // Perform a manual edit while waiting for an LSP command
 9810    // that's being run as part of a formatting code action.
 9811    let lock_guard = command_lock.lock().await;
 9812    let format = editor
 9813        .update_in(cx, |editor, window, cx| {
 9814            editor.perform_format(
 9815                project.clone(),
 9816                FormatTrigger::Manual,
 9817                FormatTarget::Buffers,
 9818                window,
 9819                cx,
 9820            )
 9821        })
 9822        .unwrap();
 9823    cx.run_until_parked();
 9824    editor.update(cx, |editor, cx| {
 9825        assert_eq!(
 9826            editor.text(cx),
 9827            r#"
 9828                applied-code-action-1-edit
 9829                applied-formatting
 9830                one
 9831                two
 9832                three
 9833            "#
 9834            .unindent()
 9835        );
 9836
 9837        editor.buffer.update(cx, |buffer, cx| {
 9838            let ix = buffer.len(cx);
 9839            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9840        });
 9841    });
 9842
 9843    // Allow the LSP command to proceed. Because the buffer was edited,
 9844    // the second code action will not be run.
 9845    drop(lock_guard);
 9846    format.await;
 9847    editor.update_in(cx, |editor, window, cx| {
 9848        assert_eq!(
 9849            editor.text(cx),
 9850            r#"
 9851                applied-code-action-1-command
 9852                applied-code-action-1-edit
 9853                applied-formatting
 9854                one
 9855                two
 9856                three
 9857                edited
 9858            "#
 9859            .unindent()
 9860        );
 9861
 9862        // The manual edit is undone first, because it is the last thing the user did
 9863        // (even though the command completed afterwards).
 9864        editor.undo(&Default::default(), window, cx);
 9865        assert_eq!(
 9866            editor.text(cx),
 9867            r#"
 9868                applied-code-action-1-command
 9869                applied-code-action-1-edit
 9870                applied-formatting
 9871                one
 9872                two
 9873                three
 9874            "#
 9875            .unindent()
 9876        );
 9877
 9878        // All the formatting (including the command, which completed after the manual edit)
 9879        // is undone together.
 9880        editor.undo(&Default::default(), window, cx);
 9881        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9882    });
 9883}
 9884
 9885#[gpui::test]
 9886async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9887    init_test(cx, |settings| {
 9888        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9889            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9890        ))
 9891    });
 9892
 9893    let fs = FakeFs::new(cx.executor());
 9894    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9895
 9896    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9897
 9898    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9899    language_registry.add(Arc::new(Language::new(
 9900        LanguageConfig {
 9901            name: "TypeScript".into(),
 9902            matcher: LanguageMatcher {
 9903                path_suffixes: vec!["ts".to_string()],
 9904                ..Default::default()
 9905            },
 9906            ..LanguageConfig::default()
 9907        },
 9908        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9909    )));
 9910    update_test_language_settings(cx, |settings| {
 9911        settings.defaults.prettier = Some(PrettierSettings {
 9912            allowed: true,
 9913            ..PrettierSettings::default()
 9914        });
 9915    });
 9916    let mut fake_servers = language_registry.register_fake_lsp(
 9917        "TypeScript",
 9918        FakeLspAdapter {
 9919            capabilities: lsp::ServerCapabilities {
 9920                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9921                ..Default::default()
 9922            },
 9923            ..Default::default()
 9924        },
 9925    );
 9926
 9927    let buffer = project
 9928        .update(cx, |project, cx| {
 9929            project.open_local_buffer(path!("/file.ts"), cx)
 9930        })
 9931        .await
 9932        .unwrap();
 9933
 9934    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9935    let (editor, cx) = cx.add_window_view(|window, cx| {
 9936        build_editor_with_project(project.clone(), buffer, window, cx)
 9937    });
 9938    editor.update_in(cx, |editor, window, cx| {
 9939        editor.set_text(
 9940            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9941            window,
 9942            cx,
 9943        )
 9944    });
 9945
 9946    cx.executor().start_waiting();
 9947    let fake_server = fake_servers.next().await.unwrap();
 9948
 9949    let format = editor
 9950        .update_in(cx, |editor, window, cx| {
 9951            editor.perform_code_action_kind(
 9952                project.clone(),
 9953                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9954                window,
 9955                cx,
 9956            )
 9957        })
 9958        .unwrap();
 9959    fake_server
 9960        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9961            assert_eq!(
 9962                params.text_document.uri,
 9963                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9964            );
 9965            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9966                lsp::CodeAction {
 9967                    title: "Organize Imports".to_string(),
 9968                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9969                    edit: Some(lsp::WorkspaceEdit {
 9970                        changes: Some(
 9971                            [(
 9972                                params.text_document.uri.clone(),
 9973                                vec![lsp::TextEdit::new(
 9974                                    lsp::Range::new(
 9975                                        lsp::Position::new(1, 0),
 9976                                        lsp::Position::new(2, 0),
 9977                                    ),
 9978                                    "".to_string(),
 9979                                )],
 9980                            )]
 9981                            .into_iter()
 9982                            .collect(),
 9983                        ),
 9984                        ..Default::default()
 9985                    }),
 9986                    ..Default::default()
 9987                },
 9988            )]))
 9989        })
 9990        .next()
 9991        .await;
 9992    cx.executor().start_waiting();
 9993    format.await;
 9994    assert_eq!(
 9995        editor.update(cx, |editor, cx| editor.text(cx)),
 9996        "import { a } from 'module';\n\nconst x = a;\n"
 9997    );
 9998
 9999    editor.update_in(cx, |editor, window, cx| {
10000        editor.set_text(
10001            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
10002            window,
10003            cx,
10004        )
10005    });
10006    // Ensure we don't lock if code action hangs.
10007    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
10008        move |params, _| async move {
10009            assert_eq!(
10010                params.text_document.uri,
10011                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
10012            );
10013            futures::future::pending::<()>().await;
10014            unreachable!()
10015        },
10016    );
10017    let format = editor
10018        .update_in(cx, |editor, window, cx| {
10019            editor.perform_code_action_kind(
10020                project,
10021                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
10022                window,
10023                cx,
10024            )
10025        })
10026        .unwrap();
10027    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
10028    cx.executor().start_waiting();
10029    format.await;
10030    assert_eq!(
10031        editor.update(cx, |editor, cx| editor.text(cx)),
10032        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
10033    );
10034}
10035
10036#[gpui::test]
10037async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
10038    init_test(cx, |_| {});
10039
10040    let mut cx = EditorLspTestContext::new_rust(
10041        lsp::ServerCapabilities {
10042            document_formatting_provider: Some(lsp::OneOf::Left(true)),
10043            ..Default::default()
10044        },
10045        cx,
10046    )
10047    .await;
10048
10049    cx.set_state(indoc! {"
10050        one.twoˇ
10051    "});
10052
10053    // The format request takes a long time. When it completes, it inserts
10054    // a newline and an indent before the `.`
10055    cx.lsp
10056        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
10057            let executor = cx.background_executor().clone();
10058            async move {
10059                executor.timer(Duration::from_millis(100)).await;
10060                Ok(Some(vec![lsp::TextEdit {
10061                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
10062                    new_text: "\n    ".into(),
10063                }]))
10064            }
10065        });
10066
10067    // Submit a format request.
10068    let format_1 = cx
10069        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
10070        .unwrap();
10071    cx.executor().run_until_parked();
10072
10073    // Submit a second format request.
10074    let format_2 = cx
10075        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
10076        .unwrap();
10077    cx.executor().run_until_parked();
10078
10079    // Wait for both format requests to complete
10080    cx.executor().advance_clock(Duration::from_millis(200));
10081    cx.executor().start_waiting();
10082    format_1.await.unwrap();
10083    cx.executor().start_waiting();
10084    format_2.await.unwrap();
10085
10086    // The formatting edits only happens once.
10087    cx.assert_editor_state(indoc! {"
10088        one
10089            .twoˇ
10090    "});
10091}
10092
10093#[gpui::test]
10094async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
10095    init_test(cx, |settings| {
10096        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10097    });
10098
10099    let mut cx = EditorLspTestContext::new_rust(
10100        lsp::ServerCapabilities {
10101            document_formatting_provider: Some(lsp::OneOf::Left(true)),
10102            ..Default::default()
10103        },
10104        cx,
10105    )
10106    .await;
10107
10108    // Set up a buffer white some trailing whitespace and no trailing newline.
10109    cx.set_state(
10110        &[
10111            "one ",   //
10112            "twoˇ",   //
10113            "three ", //
10114            "four",   //
10115        ]
10116        .join("\n"),
10117    );
10118
10119    // Submit a format request.
10120    let format = cx
10121        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
10122        .unwrap();
10123
10124    // Record which buffer changes have been sent to the language server
10125    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
10126    cx.lsp
10127        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
10128            let buffer_changes = buffer_changes.clone();
10129            move |params, _| {
10130                buffer_changes.lock().extend(
10131                    params
10132                        .content_changes
10133                        .into_iter()
10134                        .map(|e| (e.range.unwrap(), e.text)),
10135                );
10136            }
10137        });
10138
10139    // Handle formatting requests to the language server.
10140    cx.lsp
10141        .set_request_handler::<lsp::request::Formatting, _, _>({
10142            let buffer_changes = buffer_changes.clone();
10143            move |_, _| {
10144                // When formatting is requested, trailing whitespace has already been stripped,
10145                // and the trailing newline has already been added.
10146                assert_eq!(
10147                    &buffer_changes.lock()[1..],
10148                    &[
10149                        (
10150                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
10151                            "".into()
10152                        ),
10153                        (
10154                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
10155                            "".into()
10156                        ),
10157                        (
10158                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
10159                            "\n".into()
10160                        ),
10161                    ]
10162                );
10163
10164                // Insert blank lines between each line of the buffer.
10165                async move {
10166                    Ok(Some(vec![
10167                        lsp::TextEdit {
10168                            range: lsp::Range::new(
10169                                lsp::Position::new(1, 0),
10170                                lsp::Position::new(1, 0),
10171                            ),
10172                            new_text: "\n".into(),
10173                        },
10174                        lsp::TextEdit {
10175                            range: lsp::Range::new(
10176                                lsp::Position::new(2, 0),
10177                                lsp::Position::new(2, 0),
10178                            ),
10179                            new_text: "\n".into(),
10180                        },
10181                    ]))
10182                }
10183            }
10184        });
10185
10186    // After formatting the buffer, the trailing whitespace is stripped,
10187    // a newline is appended, and the edits provided by the language server
10188    // have been applied.
10189    format.await.unwrap();
10190    cx.assert_editor_state(
10191        &[
10192            "one",   //
10193            "",      //
10194            "twoˇ",  //
10195            "",      //
10196            "three", //
10197            "four",  //
10198            "",      //
10199        ]
10200        .join("\n"),
10201    );
10202
10203    // Undoing the formatting undoes the trailing whitespace removal, the
10204    // trailing newline, and the LSP edits.
10205    cx.update_buffer(|buffer, cx| buffer.undo(cx));
10206    cx.assert_editor_state(
10207        &[
10208            "one ",   //
10209            "twoˇ",   //
10210            "three ", //
10211            "four",   //
10212        ]
10213        .join("\n"),
10214    );
10215}
10216
10217#[gpui::test]
10218async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
10219    cx: &mut TestAppContext,
10220) {
10221    init_test(cx, |_| {});
10222
10223    cx.update(|cx| {
10224        cx.update_global::<SettingsStore, _>(|settings, cx| {
10225            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10226                settings.auto_signature_help = Some(true);
10227            });
10228        });
10229    });
10230
10231    let mut cx = EditorLspTestContext::new_rust(
10232        lsp::ServerCapabilities {
10233            signature_help_provider: Some(lsp::SignatureHelpOptions {
10234                ..Default::default()
10235            }),
10236            ..Default::default()
10237        },
10238        cx,
10239    )
10240    .await;
10241
10242    let language = Language::new(
10243        LanguageConfig {
10244            name: "Rust".into(),
10245            brackets: BracketPairConfig {
10246                pairs: vec![
10247                    BracketPair {
10248                        start: "{".to_string(),
10249                        end: "}".to_string(),
10250                        close: true,
10251                        surround: true,
10252                        newline: true,
10253                    },
10254                    BracketPair {
10255                        start: "(".to_string(),
10256                        end: ")".to_string(),
10257                        close: true,
10258                        surround: true,
10259                        newline: true,
10260                    },
10261                    BracketPair {
10262                        start: "/*".to_string(),
10263                        end: " */".to_string(),
10264                        close: true,
10265                        surround: true,
10266                        newline: true,
10267                    },
10268                    BracketPair {
10269                        start: "[".to_string(),
10270                        end: "]".to_string(),
10271                        close: false,
10272                        surround: false,
10273                        newline: true,
10274                    },
10275                    BracketPair {
10276                        start: "\"".to_string(),
10277                        end: "\"".to_string(),
10278                        close: true,
10279                        surround: true,
10280                        newline: false,
10281                    },
10282                    BracketPair {
10283                        start: "<".to_string(),
10284                        end: ">".to_string(),
10285                        close: false,
10286                        surround: true,
10287                        newline: true,
10288                    },
10289                ],
10290                ..Default::default()
10291            },
10292            autoclose_before: "})]".to_string(),
10293            ..Default::default()
10294        },
10295        Some(tree_sitter_rust::LANGUAGE.into()),
10296    );
10297    let language = Arc::new(language);
10298
10299    cx.language_registry().add(language.clone());
10300    cx.update_buffer(|buffer, cx| {
10301        buffer.set_language(Some(language), cx);
10302    });
10303
10304    cx.set_state(
10305        &r#"
10306            fn main() {
10307                sampleˇ
10308            }
10309        "#
10310        .unindent(),
10311    );
10312
10313    cx.update_editor(|editor, window, cx| {
10314        editor.handle_input("(", window, cx);
10315    });
10316    cx.assert_editor_state(
10317        &"
10318            fn main() {
10319                sample(ˇ)
10320            }
10321        "
10322        .unindent(),
10323    );
10324
10325    let mocked_response = lsp::SignatureHelp {
10326        signatures: vec![lsp::SignatureInformation {
10327            label: "fn sample(param1: u8, param2: u8)".to_string(),
10328            documentation: None,
10329            parameters: Some(vec![
10330                lsp::ParameterInformation {
10331                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10332                    documentation: None,
10333                },
10334                lsp::ParameterInformation {
10335                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10336                    documentation: None,
10337                },
10338            ]),
10339            active_parameter: None,
10340        }],
10341        active_signature: Some(0),
10342        active_parameter: Some(0),
10343    };
10344    handle_signature_help_request(&mut cx, mocked_response).await;
10345
10346    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10347        .await;
10348
10349    cx.editor(|editor, _, _| {
10350        let signature_help_state = editor.signature_help_state.popover().cloned();
10351        assert_eq!(
10352            signature_help_state.unwrap().label,
10353            "param1: u8, param2: u8"
10354        );
10355    });
10356}
10357
10358#[gpui::test]
10359async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
10360    init_test(cx, |_| {});
10361
10362    cx.update(|cx| {
10363        cx.update_global::<SettingsStore, _>(|settings, cx| {
10364            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10365                settings.auto_signature_help = Some(false);
10366                settings.show_signature_help_after_edits = Some(false);
10367            });
10368        });
10369    });
10370
10371    let mut cx = EditorLspTestContext::new_rust(
10372        lsp::ServerCapabilities {
10373            signature_help_provider: Some(lsp::SignatureHelpOptions {
10374                ..Default::default()
10375            }),
10376            ..Default::default()
10377        },
10378        cx,
10379    )
10380    .await;
10381
10382    let language = Language::new(
10383        LanguageConfig {
10384            name: "Rust".into(),
10385            brackets: BracketPairConfig {
10386                pairs: vec![
10387                    BracketPair {
10388                        start: "{".to_string(),
10389                        end: "}".to_string(),
10390                        close: true,
10391                        surround: true,
10392                        newline: true,
10393                    },
10394                    BracketPair {
10395                        start: "(".to_string(),
10396                        end: ")".to_string(),
10397                        close: true,
10398                        surround: true,
10399                        newline: true,
10400                    },
10401                    BracketPair {
10402                        start: "/*".to_string(),
10403                        end: " */".to_string(),
10404                        close: true,
10405                        surround: true,
10406                        newline: true,
10407                    },
10408                    BracketPair {
10409                        start: "[".to_string(),
10410                        end: "]".to_string(),
10411                        close: false,
10412                        surround: false,
10413                        newline: true,
10414                    },
10415                    BracketPair {
10416                        start: "\"".to_string(),
10417                        end: "\"".to_string(),
10418                        close: true,
10419                        surround: true,
10420                        newline: false,
10421                    },
10422                    BracketPair {
10423                        start: "<".to_string(),
10424                        end: ">".to_string(),
10425                        close: false,
10426                        surround: true,
10427                        newline: true,
10428                    },
10429                ],
10430                ..Default::default()
10431            },
10432            autoclose_before: "})]".to_string(),
10433            ..Default::default()
10434        },
10435        Some(tree_sitter_rust::LANGUAGE.into()),
10436    );
10437    let language = Arc::new(language);
10438
10439    cx.language_registry().add(language.clone());
10440    cx.update_buffer(|buffer, cx| {
10441        buffer.set_language(Some(language), cx);
10442    });
10443
10444    // Ensure that signature_help is not called when no signature help is enabled.
10445    cx.set_state(
10446        &r#"
10447            fn main() {
10448                sampleˇ
10449            }
10450        "#
10451        .unindent(),
10452    );
10453    cx.update_editor(|editor, window, cx| {
10454        editor.handle_input("(", window, cx);
10455    });
10456    cx.assert_editor_state(
10457        &"
10458            fn main() {
10459                sample(ˇ)
10460            }
10461        "
10462        .unindent(),
10463    );
10464    cx.editor(|editor, _, _| {
10465        assert!(editor.signature_help_state.task().is_none());
10466    });
10467
10468    let mocked_response = lsp::SignatureHelp {
10469        signatures: vec![lsp::SignatureInformation {
10470            label: "fn sample(param1: u8, param2: u8)".to_string(),
10471            documentation: None,
10472            parameters: Some(vec![
10473                lsp::ParameterInformation {
10474                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10475                    documentation: None,
10476                },
10477                lsp::ParameterInformation {
10478                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10479                    documentation: None,
10480                },
10481            ]),
10482            active_parameter: None,
10483        }],
10484        active_signature: Some(0),
10485        active_parameter: Some(0),
10486    };
10487
10488    // Ensure that signature_help is called when enabled afte edits
10489    cx.update(|_, cx| {
10490        cx.update_global::<SettingsStore, _>(|settings, cx| {
10491            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10492                settings.auto_signature_help = Some(false);
10493                settings.show_signature_help_after_edits = Some(true);
10494            });
10495        });
10496    });
10497    cx.set_state(
10498        &r#"
10499            fn main() {
10500                sampleˇ
10501            }
10502        "#
10503        .unindent(),
10504    );
10505    cx.update_editor(|editor, window, cx| {
10506        editor.handle_input("(", window, cx);
10507    });
10508    cx.assert_editor_state(
10509        &"
10510            fn main() {
10511                sample(ˇ)
10512            }
10513        "
10514        .unindent(),
10515    );
10516    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10517    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10518        .await;
10519    cx.update_editor(|editor, _, _| {
10520        let signature_help_state = editor.signature_help_state.popover().cloned();
10521        assert!(signature_help_state.is_some());
10522        assert_eq!(
10523            signature_help_state.unwrap().label,
10524            "param1: u8, param2: u8"
10525        );
10526        editor.signature_help_state = SignatureHelpState::default();
10527    });
10528
10529    // Ensure that signature_help is called when auto signature help override is enabled
10530    cx.update(|_, cx| {
10531        cx.update_global::<SettingsStore, _>(|settings, cx| {
10532            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10533                settings.auto_signature_help = Some(true);
10534                settings.show_signature_help_after_edits = Some(false);
10535            });
10536        });
10537    });
10538    cx.set_state(
10539        &r#"
10540            fn main() {
10541                sampleˇ
10542            }
10543        "#
10544        .unindent(),
10545    );
10546    cx.update_editor(|editor, window, cx| {
10547        editor.handle_input("(", window, cx);
10548    });
10549    cx.assert_editor_state(
10550        &"
10551            fn main() {
10552                sample(ˇ)
10553            }
10554        "
10555        .unindent(),
10556    );
10557    handle_signature_help_request(&mut cx, mocked_response).await;
10558    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10559        .await;
10560    cx.editor(|editor, _, _| {
10561        let signature_help_state = editor.signature_help_state.popover().cloned();
10562        assert!(signature_help_state.is_some());
10563        assert_eq!(
10564            signature_help_state.unwrap().label,
10565            "param1: u8, param2: u8"
10566        );
10567    });
10568}
10569
10570#[gpui::test]
10571async fn test_signature_help(cx: &mut TestAppContext) {
10572    init_test(cx, |_| {});
10573    cx.update(|cx| {
10574        cx.update_global::<SettingsStore, _>(|settings, cx| {
10575            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10576                settings.auto_signature_help = Some(true);
10577            });
10578        });
10579    });
10580
10581    let mut cx = EditorLspTestContext::new_rust(
10582        lsp::ServerCapabilities {
10583            signature_help_provider: Some(lsp::SignatureHelpOptions {
10584                ..Default::default()
10585            }),
10586            ..Default::default()
10587        },
10588        cx,
10589    )
10590    .await;
10591
10592    // A test that directly calls `show_signature_help`
10593    cx.update_editor(|editor, window, cx| {
10594        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10595    });
10596
10597    let mocked_response = lsp::SignatureHelp {
10598        signatures: vec![lsp::SignatureInformation {
10599            label: "fn sample(param1: u8, param2: u8)".to_string(),
10600            documentation: None,
10601            parameters: Some(vec![
10602                lsp::ParameterInformation {
10603                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10604                    documentation: None,
10605                },
10606                lsp::ParameterInformation {
10607                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10608                    documentation: None,
10609                },
10610            ]),
10611            active_parameter: None,
10612        }],
10613        active_signature: Some(0),
10614        active_parameter: Some(0),
10615    };
10616    handle_signature_help_request(&mut cx, mocked_response).await;
10617
10618    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10619        .await;
10620
10621    cx.editor(|editor, _, _| {
10622        let signature_help_state = editor.signature_help_state.popover().cloned();
10623        assert!(signature_help_state.is_some());
10624        assert_eq!(
10625            signature_help_state.unwrap().label,
10626            "param1: u8, param2: u8"
10627        );
10628    });
10629
10630    // When exiting outside from inside the brackets, `signature_help` is closed.
10631    cx.set_state(indoc! {"
10632        fn main() {
10633            sample(ˇ);
10634        }
10635
10636        fn sample(param1: u8, param2: u8) {}
10637    "});
10638
10639    cx.update_editor(|editor, window, cx| {
10640        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10641    });
10642
10643    let mocked_response = lsp::SignatureHelp {
10644        signatures: Vec::new(),
10645        active_signature: None,
10646        active_parameter: None,
10647    };
10648    handle_signature_help_request(&mut cx, mocked_response).await;
10649
10650    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10651        .await;
10652
10653    cx.editor(|editor, _, _| {
10654        assert!(!editor.signature_help_state.is_shown());
10655    });
10656
10657    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10658    cx.set_state(indoc! {"
10659        fn main() {
10660            sample(ˇ);
10661        }
10662
10663        fn sample(param1: u8, param2: u8) {}
10664    "});
10665
10666    let mocked_response = lsp::SignatureHelp {
10667        signatures: vec![lsp::SignatureInformation {
10668            label: "fn sample(param1: u8, param2: u8)".to_string(),
10669            documentation: None,
10670            parameters: Some(vec![
10671                lsp::ParameterInformation {
10672                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10673                    documentation: None,
10674                },
10675                lsp::ParameterInformation {
10676                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10677                    documentation: None,
10678                },
10679            ]),
10680            active_parameter: None,
10681        }],
10682        active_signature: Some(0),
10683        active_parameter: Some(0),
10684    };
10685    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10686    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10687        .await;
10688    cx.editor(|editor, _, _| {
10689        assert!(editor.signature_help_state.is_shown());
10690    });
10691
10692    // Restore the popover with more parameter input
10693    cx.set_state(indoc! {"
10694        fn main() {
10695            sample(param1, param2ˇ);
10696        }
10697
10698        fn sample(param1: u8, param2: u8) {}
10699    "});
10700
10701    let mocked_response = lsp::SignatureHelp {
10702        signatures: vec![lsp::SignatureInformation {
10703            label: "fn sample(param1: u8, param2: u8)".to_string(),
10704            documentation: None,
10705            parameters: Some(vec![
10706                lsp::ParameterInformation {
10707                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10708                    documentation: None,
10709                },
10710                lsp::ParameterInformation {
10711                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10712                    documentation: None,
10713                },
10714            ]),
10715            active_parameter: None,
10716        }],
10717        active_signature: Some(0),
10718        active_parameter: Some(1),
10719    };
10720    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10721    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10722        .await;
10723
10724    // When selecting a range, the popover is gone.
10725    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10726    cx.update_editor(|editor, window, cx| {
10727        editor.change_selections(None, window, cx, |s| {
10728            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10729        })
10730    });
10731    cx.assert_editor_state(indoc! {"
10732        fn main() {
10733            sample(param1, «ˇparam2»);
10734        }
10735
10736        fn sample(param1: u8, param2: u8) {}
10737    "});
10738    cx.editor(|editor, _, _| {
10739        assert!(!editor.signature_help_state.is_shown());
10740    });
10741
10742    // When unselecting again, the popover is back if within the brackets.
10743    cx.update_editor(|editor, window, cx| {
10744        editor.change_selections(None, window, cx, |s| {
10745            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10746        })
10747    });
10748    cx.assert_editor_state(indoc! {"
10749        fn main() {
10750            sample(param1, ˇparam2);
10751        }
10752
10753        fn sample(param1: u8, param2: u8) {}
10754    "});
10755    handle_signature_help_request(&mut cx, mocked_response).await;
10756    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10757        .await;
10758    cx.editor(|editor, _, _| {
10759        assert!(editor.signature_help_state.is_shown());
10760    });
10761
10762    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10763    cx.update_editor(|editor, window, cx| {
10764        editor.change_selections(None, window, cx, |s| {
10765            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10766            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10767        })
10768    });
10769    cx.assert_editor_state(indoc! {"
10770        fn main() {
10771            sample(param1, ˇparam2);
10772        }
10773
10774        fn sample(param1: u8, param2: u8) {}
10775    "});
10776
10777    let mocked_response = lsp::SignatureHelp {
10778        signatures: vec![lsp::SignatureInformation {
10779            label: "fn sample(param1: u8, param2: u8)".to_string(),
10780            documentation: None,
10781            parameters: Some(vec![
10782                lsp::ParameterInformation {
10783                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10784                    documentation: None,
10785                },
10786                lsp::ParameterInformation {
10787                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10788                    documentation: None,
10789                },
10790            ]),
10791            active_parameter: None,
10792        }],
10793        active_signature: Some(0),
10794        active_parameter: Some(1),
10795    };
10796    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10797    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10798        .await;
10799    cx.update_editor(|editor, _, cx| {
10800        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10801    });
10802    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10803        .await;
10804    cx.update_editor(|editor, window, cx| {
10805        editor.change_selections(None, window, cx, |s| {
10806            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10807        })
10808    });
10809    cx.assert_editor_state(indoc! {"
10810        fn main() {
10811            sample(param1, «ˇparam2»);
10812        }
10813
10814        fn sample(param1: u8, param2: u8) {}
10815    "});
10816    cx.update_editor(|editor, window, cx| {
10817        editor.change_selections(None, window, cx, |s| {
10818            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10819        })
10820    });
10821    cx.assert_editor_state(indoc! {"
10822        fn main() {
10823            sample(param1, ˇparam2);
10824        }
10825
10826        fn sample(param1: u8, param2: u8) {}
10827    "});
10828    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10829        .await;
10830}
10831
10832#[gpui::test]
10833async fn test_completion_mode(cx: &mut TestAppContext) {
10834    init_test(cx, |_| {});
10835    let mut cx = EditorLspTestContext::new_rust(
10836        lsp::ServerCapabilities {
10837            completion_provider: Some(lsp::CompletionOptions {
10838                resolve_provider: Some(true),
10839                ..Default::default()
10840            }),
10841            ..Default::default()
10842        },
10843        cx,
10844    )
10845    .await;
10846
10847    struct Run {
10848        run_description: &'static str,
10849        initial_state: String,
10850        buffer_marked_text: String,
10851        completion_label: &'static str,
10852        completion_text: &'static str,
10853        expected_with_insert_mode: String,
10854        expected_with_replace_mode: String,
10855        expected_with_replace_subsequence_mode: String,
10856        expected_with_replace_suffix_mode: String,
10857    }
10858
10859    let runs = [
10860        Run {
10861            run_description: "Start of word matches completion text",
10862            initial_state: "before ediˇ after".into(),
10863            buffer_marked_text: "before <edi|> after".into(),
10864            completion_label: "editor",
10865            completion_text: "editor",
10866            expected_with_insert_mode: "before editorˇ after".into(),
10867            expected_with_replace_mode: "before editorˇ after".into(),
10868            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10869            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10870        },
10871        Run {
10872            run_description: "Accept same text at the middle of the word",
10873            initial_state: "before ediˇtor after".into(),
10874            buffer_marked_text: "before <edi|tor> after".into(),
10875            completion_label: "editor",
10876            completion_text: "editor",
10877            expected_with_insert_mode: "before editorˇtor after".into(),
10878            expected_with_replace_mode: "before editorˇ after".into(),
10879            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10880            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10881        },
10882        Run {
10883            run_description: "End of word matches completion text -- cursor at end",
10884            initial_state: "before torˇ after".into(),
10885            buffer_marked_text: "before <tor|> after".into(),
10886            completion_label: "editor",
10887            completion_text: "editor",
10888            expected_with_insert_mode: "before editorˇ after".into(),
10889            expected_with_replace_mode: "before editorˇ after".into(),
10890            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10891            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10892        },
10893        Run {
10894            run_description: "End of word matches completion text -- cursor at start",
10895            initial_state: "before ˇtor after".into(),
10896            buffer_marked_text: "before <|tor> after".into(),
10897            completion_label: "editor",
10898            completion_text: "editor",
10899            expected_with_insert_mode: "before editorˇtor after".into(),
10900            expected_with_replace_mode: "before editorˇ after".into(),
10901            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10902            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10903        },
10904        Run {
10905            run_description: "Prepend text containing whitespace",
10906            initial_state: "pˇfield: bool".into(),
10907            buffer_marked_text: "<p|field>: bool".into(),
10908            completion_label: "pub ",
10909            completion_text: "pub ",
10910            expected_with_insert_mode: "pub ˇfield: bool".into(),
10911            expected_with_replace_mode: "pub ˇ: bool".into(),
10912            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10913            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10914        },
10915        Run {
10916            run_description: "Add element to start of list",
10917            initial_state: "[element_ˇelement_2]".into(),
10918            buffer_marked_text: "[<element_|element_2>]".into(),
10919            completion_label: "element_1",
10920            completion_text: "element_1",
10921            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10922            expected_with_replace_mode: "[element_1ˇ]".into(),
10923            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10924            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10925        },
10926        Run {
10927            run_description: "Add element to start of list -- first and second elements are equal",
10928            initial_state: "[elˇelement]".into(),
10929            buffer_marked_text: "[<el|element>]".into(),
10930            completion_label: "element",
10931            completion_text: "element",
10932            expected_with_insert_mode: "[elementˇelement]".into(),
10933            expected_with_replace_mode: "[elementˇ]".into(),
10934            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10935            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10936        },
10937        Run {
10938            run_description: "Ends with matching suffix",
10939            initial_state: "SubˇError".into(),
10940            buffer_marked_text: "<Sub|Error>".into(),
10941            completion_label: "SubscriptionError",
10942            completion_text: "SubscriptionError",
10943            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10944            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10945            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10946            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10947        },
10948        Run {
10949            run_description: "Suffix is a subsequence -- contiguous",
10950            initial_state: "SubˇErr".into(),
10951            buffer_marked_text: "<Sub|Err>".into(),
10952            completion_label: "SubscriptionError",
10953            completion_text: "SubscriptionError",
10954            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10955            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10956            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10957            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10958        },
10959        Run {
10960            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10961            initial_state: "Suˇscrirr".into(),
10962            buffer_marked_text: "<Su|scrirr>".into(),
10963            completion_label: "SubscriptionError",
10964            completion_text: "SubscriptionError",
10965            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10966            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10967            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10968            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10969        },
10970        Run {
10971            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10972            initial_state: "foo(indˇix)".into(),
10973            buffer_marked_text: "foo(<ind|ix>)".into(),
10974            completion_label: "node_index",
10975            completion_text: "node_index",
10976            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10977            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10978            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10979            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10980        },
10981        Run {
10982            run_description: "Replace range ends before cursor - should extend to cursor",
10983            initial_state: "before editˇo after".into(),
10984            buffer_marked_text: "before <{ed}>it|o after".into(),
10985            completion_label: "editor",
10986            completion_text: "editor",
10987            expected_with_insert_mode: "before editorˇo after".into(),
10988            expected_with_replace_mode: "before editorˇo after".into(),
10989            expected_with_replace_subsequence_mode: "before editorˇo after".into(),
10990            expected_with_replace_suffix_mode: "before editorˇo after".into(),
10991        },
10992        Run {
10993            run_description: "Uses label for suffix matching",
10994            initial_state: "before ediˇtor after".into(),
10995            buffer_marked_text: "before <edi|tor> after".into(),
10996            completion_label: "editor",
10997            completion_text: "editor()",
10998            expected_with_insert_mode: "before editor()ˇtor after".into(),
10999            expected_with_replace_mode: "before editor()ˇ after".into(),
11000            expected_with_replace_subsequence_mode: "before editor()ˇ after".into(),
11001            expected_with_replace_suffix_mode: "before editor()ˇ after".into(),
11002        },
11003        Run {
11004            run_description: "Case insensitive subsequence and suffix matching",
11005            initial_state: "before EDiˇtoR after".into(),
11006            buffer_marked_text: "before <EDi|toR> after".into(),
11007            completion_label: "editor",
11008            completion_text: "editor",
11009            expected_with_insert_mode: "before editorˇtoR after".into(),
11010            expected_with_replace_mode: "before editorˇ after".into(),
11011            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
11012            expected_with_replace_suffix_mode: "before editorˇ after".into(),
11013        },
11014    ];
11015
11016    for run in runs {
11017        let run_variations = [
11018            (LspInsertMode::Insert, run.expected_with_insert_mode),
11019            (LspInsertMode::Replace, run.expected_with_replace_mode),
11020            (
11021                LspInsertMode::ReplaceSubsequence,
11022                run.expected_with_replace_subsequence_mode,
11023            ),
11024            (
11025                LspInsertMode::ReplaceSuffix,
11026                run.expected_with_replace_suffix_mode,
11027            ),
11028        ];
11029
11030        for (lsp_insert_mode, expected_text) in run_variations {
11031            eprintln!(
11032                "run = {:?}, mode = {lsp_insert_mode:.?}",
11033                run.run_description,
11034            );
11035
11036            update_test_language_settings(&mut cx, |settings| {
11037                settings.defaults.completions = Some(CompletionSettings {
11038                    lsp_insert_mode,
11039                    words: WordsCompletionMode::Disabled,
11040                    lsp: true,
11041                    lsp_fetch_timeout_ms: 0,
11042                });
11043            });
11044
11045            cx.set_state(&run.initial_state);
11046            cx.update_editor(|editor, window, cx| {
11047                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11048            });
11049
11050            let counter = Arc::new(AtomicUsize::new(0));
11051            handle_completion_request_with_insert_and_replace(
11052                &mut cx,
11053                &run.buffer_marked_text,
11054                vec![(run.completion_label, run.completion_text)],
11055                counter.clone(),
11056            )
11057            .await;
11058            cx.condition(|editor, _| editor.context_menu_visible())
11059                .await;
11060            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11061
11062            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11063                editor
11064                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
11065                    .unwrap()
11066            });
11067            cx.assert_editor_state(&expected_text);
11068            handle_resolve_completion_request(&mut cx, None).await;
11069            apply_additional_edits.await.unwrap();
11070        }
11071    }
11072}
11073
11074#[gpui::test]
11075async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
11076    init_test(cx, |_| {});
11077    let mut cx = EditorLspTestContext::new_rust(
11078        lsp::ServerCapabilities {
11079            completion_provider: Some(lsp::CompletionOptions {
11080                resolve_provider: Some(true),
11081                ..Default::default()
11082            }),
11083            ..Default::default()
11084        },
11085        cx,
11086    )
11087    .await;
11088
11089    let initial_state = "SubˇError";
11090    let buffer_marked_text = "<Sub|Error>";
11091    let completion_text = "SubscriptionError";
11092    let expected_with_insert_mode = "SubscriptionErrorˇError";
11093    let expected_with_replace_mode = "SubscriptionErrorˇ";
11094
11095    update_test_language_settings(&mut cx, |settings| {
11096        settings.defaults.completions = Some(CompletionSettings {
11097            words: WordsCompletionMode::Disabled,
11098            // set the opposite here to ensure that the action is overriding the default behavior
11099            lsp_insert_mode: LspInsertMode::Insert,
11100            lsp: true,
11101            lsp_fetch_timeout_ms: 0,
11102        });
11103    });
11104
11105    cx.set_state(initial_state);
11106    cx.update_editor(|editor, window, cx| {
11107        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11108    });
11109
11110    let counter = Arc::new(AtomicUsize::new(0));
11111    handle_completion_request_with_insert_and_replace(
11112        &mut cx,
11113        &buffer_marked_text,
11114        vec![(completion_text, completion_text)],
11115        counter.clone(),
11116    )
11117    .await;
11118    cx.condition(|editor, _| editor.context_menu_visible())
11119        .await;
11120    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11121
11122    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11123        editor
11124            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11125            .unwrap()
11126    });
11127    cx.assert_editor_state(&expected_with_replace_mode);
11128    handle_resolve_completion_request(&mut cx, None).await;
11129    apply_additional_edits.await.unwrap();
11130
11131    update_test_language_settings(&mut cx, |settings| {
11132        settings.defaults.completions = Some(CompletionSettings {
11133            words: WordsCompletionMode::Disabled,
11134            // set the opposite here to ensure that the action is overriding the default behavior
11135            lsp_insert_mode: LspInsertMode::Replace,
11136            lsp: true,
11137            lsp_fetch_timeout_ms: 0,
11138        });
11139    });
11140
11141    cx.set_state(initial_state);
11142    cx.update_editor(|editor, window, cx| {
11143        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11144    });
11145    handle_completion_request_with_insert_and_replace(
11146        &mut cx,
11147        &buffer_marked_text,
11148        vec![(completion_text, completion_text)],
11149        counter.clone(),
11150    )
11151    .await;
11152    cx.condition(|editor, _| editor.context_menu_visible())
11153        .await;
11154    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11155
11156    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11157        editor
11158            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
11159            .unwrap()
11160    });
11161    cx.assert_editor_state(&expected_with_insert_mode);
11162    handle_resolve_completion_request(&mut cx, None).await;
11163    apply_additional_edits.await.unwrap();
11164}
11165
11166#[gpui::test]
11167async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
11168    init_test(cx, |_| {});
11169    let mut cx = EditorLspTestContext::new_rust(
11170        lsp::ServerCapabilities {
11171            completion_provider: Some(lsp::CompletionOptions {
11172                resolve_provider: Some(true),
11173                ..Default::default()
11174            }),
11175            ..Default::default()
11176        },
11177        cx,
11178    )
11179    .await;
11180
11181    // scenario: surrounding text matches completion text
11182    let completion_text = "to_offset";
11183    let initial_state = indoc! {"
11184        1. buf.to_offˇsuffix
11185        2. buf.to_offˇsuf
11186        3. buf.to_offˇfix
11187        4. buf.to_offˇ
11188        5. into_offˇensive
11189        6. ˇsuffix
11190        7. let ˇ //
11191        8. aaˇzz
11192        9. buf.to_off«zzzzzˇ»suffix
11193        10. buf.«ˇzzzzz»suffix
11194        11. to_off«ˇzzzzz»
11195
11196        buf.to_offˇsuffix  // newest cursor
11197    "};
11198    let completion_marked_buffer = indoc! {"
11199        1. buf.to_offsuffix
11200        2. buf.to_offsuf
11201        3. buf.to_offfix
11202        4. buf.to_off
11203        5. into_offensive
11204        6. suffix
11205        7. let  //
11206        8. aazz
11207        9. buf.to_offzzzzzsuffix
11208        10. buf.zzzzzsuffix
11209        11. to_offzzzzz
11210
11211        buf.<to_off|suffix>  // newest cursor
11212    "};
11213    let expected = indoc! {"
11214        1. buf.to_offsetˇ
11215        2. buf.to_offsetˇsuf
11216        3. buf.to_offsetˇfix
11217        4. buf.to_offsetˇ
11218        5. into_offsetˇensive
11219        6. to_offsetˇsuffix
11220        7. let to_offsetˇ //
11221        8. aato_offsetˇzz
11222        9. buf.to_offsetˇ
11223        10. buf.to_offsetˇsuffix
11224        11. to_offsetˇ
11225
11226        buf.to_offsetˇ  // newest cursor
11227    "};
11228    cx.set_state(initial_state);
11229    cx.update_editor(|editor, window, cx| {
11230        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11231    });
11232    handle_completion_request_with_insert_and_replace(
11233        &mut cx,
11234        completion_marked_buffer,
11235        vec![(completion_text, completion_text)],
11236        Arc::new(AtomicUsize::new(0)),
11237    )
11238    .await;
11239    cx.condition(|editor, _| editor.context_menu_visible())
11240        .await;
11241    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11242        editor
11243            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11244            .unwrap()
11245    });
11246    cx.assert_editor_state(expected);
11247    handle_resolve_completion_request(&mut cx, None).await;
11248    apply_additional_edits.await.unwrap();
11249
11250    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
11251    let completion_text = "foo_and_bar";
11252    let initial_state = indoc! {"
11253        1. ooanbˇ
11254        2. zooanbˇ
11255        3. ooanbˇz
11256        4. zooanbˇz
11257        5. ooanˇ
11258        6. oanbˇ
11259
11260        ooanbˇ
11261    "};
11262    let completion_marked_buffer = indoc! {"
11263        1. ooanb
11264        2. zooanb
11265        3. ooanbz
11266        4. zooanbz
11267        5. ooan
11268        6. oanb
11269
11270        <ooanb|>
11271    "};
11272    let expected = indoc! {"
11273        1. foo_and_barˇ
11274        2. zfoo_and_barˇ
11275        3. foo_and_barˇz
11276        4. zfoo_and_barˇz
11277        5. ooanfoo_and_barˇ
11278        6. oanbfoo_and_barˇ
11279
11280        foo_and_barˇ
11281    "};
11282    cx.set_state(initial_state);
11283    cx.update_editor(|editor, window, cx| {
11284        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11285    });
11286    handle_completion_request_with_insert_and_replace(
11287        &mut cx,
11288        completion_marked_buffer,
11289        vec![(completion_text, completion_text)],
11290        Arc::new(AtomicUsize::new(0)),
11291    )
11292    .await;
11293    cx.condition(|editor, _| editor.context_menu_visible())
11294        .await;
11295    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11296        editor
11297            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11298            .unwrap()
11299    });
11300    cx.assert_editor_state(expected);
11301    handle_resolve_completion_request(&mut cx, None).await;
11302    apply_additional_edits.await.unwrap();
11303
11304    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
11305    // (expects the same as if it was inserted at the end)
11306    let completion_text = "foo_and_bar";
11307    let initial_state = indoc! {"
11308        1. ooˇanb
11309        2. zooˇanb
11310        3. ooˇanbz
11311        4. zooˇanbz
11312
11313        ooˇanb
11314    "};
11315    let completion_marked_buffer = indoc! {"
11316        1. ooanb
11317        2. zooanb
11318        3. ooanbz
11319        4. zooanbz
11320
11321        <oo|anb>
11322    "};
11323    let expected = indoc! {"
11324        1. foo_and_barˇ
11325        2. zfoo_and_barˇ
11326        3. foo_and_barˇz
11327        4. zfoo_and_barˇz
11328
11329        foo_and_barˇ
11330    "};
11331    cx.set_state(initial_state);
11332    cx.update_editor(|editor, window, cx| {
11333        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11334    });
11335    handle_completion_request_with_insert_and_replace(
11336        &mut cx,
11337        completion_marked_buffer,
11338        vec![(completion_text, completion_text)],
11339        Arc::new(AtomicUsize::new(0)),
11340    )
11341    .await;
11342    cx.condition(|editor, _| editor.context_menu_visible())
11343        .await;
11344    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11345        editor
11346            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11347            .unwrap()
11348    });
11349    cx.assert_editor_state(expected);
11350    handle_resolve_completion_request(&mut cx, None).await;
11351    apply_additional_edits.await.unwrap();
11352}
11353
11354// This used to crash
11355#[gpui::test]
11356async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
11357    init_test(cx, |_| {});
11358
11359    let buffer_text = indoc! {"
11360        fn main() {
11361            10.satu;
11362
11363            //
11364            // separate cursors so they open in different excerpts (manually reproducible)
11365            //
11366
11367            10.satu20;
11368        }
11369    "};
11370    let multibuffer_text_with_selections = indoc! {"
11371        fn main() {
11372            10.satuˇ;
11373
11374            //
11375
11376            //
11377
11378            10.satuˇ20;
11379        }
11380    "};
11381    let expected_multibuffer = indoc! {"
11382        fn main() {
11383            10.saturating_sub()ˇ;
11384
11385            //
11386
11387            //
11388
11389            10.saturating_sub()ˇ;
11390        }
11391    "};
11392
11393    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
11394    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
11395
11396    let fs = FakeFs::new(cx.executor());
11397    fs.insert_tree(
11398        path!("/a"),
11399        json!({
11400            "main.rs": buffer_text,
11401        }),
11402    )
11403    .await;
11404
11405    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11406    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11407    language_registry.add(rust_lang());
11408    let mut fake_servers = language_registry.register_fake_lsp(
11409        "Rust",
11410        FakeLspAdapter {
11411            capabilities: lsp::ServerCapabilities {
11412                completion_provider: Some(lsp::CompletionOptions {
11413                    resolve_provider: None,
11414                    ..lsp::CompletionOptions::default()
11415                }),
11416                ..lsp::ServerCapabilities::default()
11417            },
11418            ..FakeLspAdapter::default()
11419        },
11420    );
11421    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11422    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11423    let buffer = project
11424        .update(cx, |project, cx| {
11425            project.open_local_buffer(path!("/a/main.rs"), cx)
11426        })
11427        .await
11428        .unwrap();
11429
11430    let multi_buffer = cx.new(|cx| {
11431        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
11432        multi_buffer.push_excerpts(
11433            buffer.clone(),
11434            [ExcerptRange::new(0..first_excerpt_end)],
11435            cx,
11436        );
11437        multi_buffer.push_excerpts(
11438            buffer.clone(),
11439            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
11440            cx,
11441        );
11442        multi_buffer
11443    });
11444
11445    let editor = workspace
11446        .update(cx, |_, window, cx| {
11447            cx.new(|cx| {
11448                Editor::new(
11449                    EditorMode::Full {
11450                        scale_ui_elements_with_buffer_font_size: false,
11451                        show_active_line_background: false,
11452                        sized_by_content: false,
11453                    },
11454                    multi_buffer.clone(),
11455                    Some(project.clone()),
11456                    window,
11457                    cx,
11458                )
11459            })
11460        })
11461        .unwrap();
11462
11463    let pane = workspace
11464        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11465        .unwrap();
11466    pane.update_in(cx, |pane, window, cx| {
11467        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
11468    });
11469
11470    let fake_server = fake_servers.next().await.unwrap();
11471
11472    editor.update_in(cx, |editor, window, cx| {
11473        editor.change_selections(None, window, cx, |s| {
11474            s.select_ranges([
11475                Point::new(1, 11)..Point::new(1, 11),
11476                Point::new(7, 11)..Point::new(7, 11),
11477            ])
11478        });
11479
11480        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
11481    });
11482
11483    editor.update_in(cx, |editor, window, cx| {
11484        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11485    });
11486
11487    fake_server
11488        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11489            let completion_item = lsp::CompletionItem {
11490                label: "saturating_sub()".into(),
11491                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11492                    lsp::InsertReplaceEdit {
11493                        new_text: "saturating_sub()".to_owned(),
11494                        insert: lsp::Range::new(
11495                            lsp::Position::new(7, 7),
11496                            lsp::Position::new(7, 11),
11497                        ),
11498                        replace: lsp::Range::new(
11499                            lsp::Position::new(7, 7),
11500                            lsp::Position::new(7, 13),
11501                        ),
11502                    },
11503                )),
11504                ..lsp::CompletionItem::default()
11505            };
11506
11507            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
11508        })
11509        .next()
11510        .await
11511        .unwrap();
11512
11513    cx.condition(&editor, |editor, _| editor.context_menu_visible())
11514        .await;
11515
11516    editor
11517        .update_in(cx, |editor, window, cx| {
11518            editor
11519                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11520                .unwrap()
11521        })
11522        .await
11523        .unwrap();
11524
11525    editor.update(cx, |editor, cx| {
11526        assert_text_with_selections(editor, expected_multibuffer, cx);
11527    })
11528}
11529
11530#[gpui::test]
11531async fn test_completion(cx: &mut TestAppContext) {
11532    init_test(cx, |_| {});
11533
11534    let mut cx = EditorLspTestContext::new_rust(
11535        lsp::ServerCapabilities {
11536            completion_provider: Some(lsp::CompletionOptions {
11537                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11538                resolve_provider: Some(true),
11539                ..Default::default()
11540            }),
11541            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11542            ..Default::default()
11543        },
11544        cx,
11545    )
11546    .await;
11547    let counter = Arc::new(AtomicUsize::new(0));
11548
11549    cx.set_state(indoc! {"
11550        oneˇ
11551        two
11552        three
11553    "});
11554    cx.simulate_keystroke(".");
11555    handle_completion_request(
11556        indoc! {"
11557            one.|<>
11558            two
11559            three
11560        "},
11561        vec!["first_completion", "second_completion"],
11562        true,
11563        counter.clone(),
11564        &mut cx,
11565    )
11566    .await;
11567    cx.condition(|editor, _| editor.context_menu_visible())
11568        .await;
11569    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11570
11571    let _handler = handle_signature_help_request(
11572        &mut cx,
11573        lsp::SignatureHelp {
11574            signatures: vec![lsp::SignatureInformation {
11575                label: "test signature".to_string(),
11576                documentation: None,
11577                parameters: Some(vec![lsp::ParameterInformation {
11578                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
11579                    documentation: None,
11580                }]),
11581                active_parameter: None,
11582            }],
11583            active_signature: None,
11584            active_parameter: None,
11585        },
11586    );
11587    cx.update_editor(|editor, window, cx| {
11588        assert!(
11589            !editor.signature_help_state.is_shown(),
11590            "No signature help was called for"
11591        );
11592        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11593    });
11594    cx.run_until_parked();
11595    cx.update_editor(|editor, _, _| {
11596        assert!(
11597            !editor.signature_help_state.is_shown(),
11598            "No signature help should be shown when completions menu is open"
11599        );
11600    });
11601
11602    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11603        editor.context_menu_next(&Default::default(), window, cx);
11604        editor
11605            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11606            .unwrap()
11607    });
11608    cx.assert_editor_state(indoc! {"
11609        one.second_completionˇ
11610        two
11611        three
11612    "});
11613
11614    handle_resolve_completion_request(
11615        &mut cx,
11616        Some(vec![
11617            (
11618                //This overlaps with the primary completion edit which is
11619                //misbehavior from the LSP spec, test that we filter it out
11620                indoc! {"
11621                    one.second_ˇcompletion
11622                    two
11623                    threeˇ
11624                "},
11625                "overlapping additional edit",
11626            ),
11627            (
11628                indoc! {"
11629                    one.second_completion
11630                    two
11631                    threeˇ
11632                "},
11633                "\nadditional edit",
11634            ),
11635        ]),
11636    )
11637    .await;
11638    apply_additional_edits.await.unwrap();
11639    cx.assert_editor_state(indoc! {"
11640        one.second_completionˇ
11641        two
11642        three
11643        additional edit
11644    "});
11645
11646    cx.set_state(indoc! {"
11647        one.second_completion
11648        twoˇ
11649        threeˇ
11650        additional edit
11651    "});
11652    cx.simulate_keystroke(" ");
11653    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11654    cx.simulate_keystroke("s");
11655    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11656
11657    cx.assert_editor_state(indoc! {"
11658        one.second_completion
11659        two sˇ
11660        three sˇ
11661        additional edit
11662    "});
11663    handle_completion_request(
11664        indoc! {"
11665            one.second_completion
11666            two s
11667            three <s|>
11668            additional edit
11669        "},
11670        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11671        true,
11672        counter.clone(),
11673        &mut cx,
11674    )
11675    .await;
11676    cx.condition(|editor, _| editor.context_menu_visible())
11677        .await;
11678    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11679
11680    cx.simulate_keystroke("i");
11681
11682    handle_completion_request(
11683        indoc! {"
11684            one.second_completion
11685            two si
11686            three <si|>
11687            additional edit
11688        "},
11689        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11690        true,
11691        counter.clone(),
11692        &mut cx,
11693    )
11694    .await;
11695    cx.condition(|editor, _| editor.context_menu_visible())
11696        .await;
11697    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11698
11699    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11700        editor
11701            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11702            .unwrap()
11703    });
11704    cx.assert_editor_state(indoc! {"
11705        one.second_completion
11706        two sixth_completionˇ
11707        three sixth_completionˇ
11708        additional edit
11709    "});
11710
11711    apply_additional_edits.await.unwrap();
11712
11713    update_test_language_settings(&mut cx, |settings| {
11714        settings.defaults.show_completions_on_input = Some(false);
11715    });
11716    cx.set_state("editorˇ");
11717    cx.simulate_keystroke(".");
11718    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11719    cx.simulate_keystrokes("c l o");
11720    cx.assert_editor_state("editor.cloˇ");
11721    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11722    cx.update_editor(|editor, window, cx| {
11723        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11724    });
11725    handle_completion_request(
11726        "editor.<clo|>",
11727        vec!["close", "clobber"],
11728        true,
11729        counter.clone(),
11730        &mut cx,
11731    )
11732    .await;
11733    cx.condition(|editor, _| editor.context_menu_visible())
11734        .await;
11735    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11736
11737    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11738        editor
11739            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11740            .unwrap()
11741    });
11742    cx.assert_editor_state("editor.closeˇ");
11743    handle_resolve_completion_request(&mut cx, None).await;
11744    apply_additional_edits.await.unwrap();
11745}
11746
11747#[gpui::test]
11748async fn test_completion_reuse(cx: &mut TestAppContext) {
11749    init_test(cx, |_| {});
11750
11751    let mut cx = EditorLspTestContext::new_rust(
11752        lsp::ServerCapabilities {
11753            completion_provider: Some(lsp::CompletionOptions {
11754                trigger_characters: Some(vec![".".to_string()]),
11755                ..Default::default()
11756            }),
11757            ..Default::default()
11758        },
11759        cx,
11760    )
11761    .await;
11762
11763    let counter = Arc::new(AtomicUsize::new(0));
11764    cx.set_state("objˇ");
11765    cx.simulate_keystroke(".");
11766
11767    // Initial completion request returns complete results
11768    let is_incomplete = false;
11769    handle_completion_request(
11770        "obj.|<>",
11771        vec!["a", "ab", "abc"],
11772        is_incomplete,
11773        counter.clone(),
11774        &mut cx,
11775    )
11776    .await;
11777    cx.run_until_parked();
11778    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11779    cx.assert_editor_state("obj.ˇ");
11780    check_displayed_completions(vec!["a", "ab", "abc"], &mut cx);
11781
11782    // Type "a" - filters existing completions
11783    cx.simulate_keystroke("a");
11784    cx.run_until_parked();
11785    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11786    cx.assert_editor_state("obj.aˇ");
11787    check_displayed_completions(vec!["a", "ab", "abc"], &mut cx);
11788
11789    // Type "b" - filters existing completions
11790    cx.simulate_keystroke("b");
11791    cx.run_until_parked();
11792    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11793    cx.assert_editor_state("obj.abˇ");
11794    check_displayed_completions(vec!["ab", "abc"], &mut cx);
11795
11796    // Type "c" - filters existing completions
11797    cx.simulate_keystroke("c");
11798    cx.run_until_parked();
11799    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11800    cx.assert_editor_state("obj.abcˇ");
11801    check_displayed_completions(vec!["abc"], &mut cx);
11802
11803    // Backspace to delete "c" - filters existing completions
11804    cx.update_editor(|editor, window, cx| {
11805        editor.backspace(&Backspace, window, cx);
11806    });
11807    cx.run_until_parked();
11808    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11809    cx.assert_editor_state("obj.abˇ");
11810    check_displayed_completions(vec!["ab", "abc"], &mut cx);
11811
11812    // Moving cursor to the left dismisses menu.
11813    cx.update_editor(|editor, window, cx| {
11814        editor.move_left(&MoveLeft, window, cx);
11815    });
11816    cx.run_until_parked();
11817    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11818    cx.assert_editor_state("obj.aˇb");
11819    cx.update_editor(|editor, _, _| {
11820        assert_eq!(editor.context_menu_visible(), false);
11821    });
11822
11823    // Type "b" - new request
11824    cx.simulate_keystroke("b");
11825    let is_incomplete = false;
11826    handle_completion_request(
11827        "obj.<ab|>a",
11828        vec!["ab", "abc"],
11829        is_incomplete,
11830        counter.clone(),
11831        &mut cx,
11832    )
11833    .await;
11834    cx.run_until_parked();
11835    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11836    cx.assert_editor_state("obj.abˇb");
11837    check_displayed_completions(vec!["ab", "abc"], &mut cx);
11838
11839    // Backspace to delete "b" - since query was "ab" and is now "a", new request is made.
11840    cx.update_editor(|editor, window, cx| {
11841        editor.backspace(&Backspace, window, cx);
11842    });
11843    let is_incomplete = false;
11844    handle_completion_request(
11845        "obj.<a|>b",
11846        vec!["a", "ab", "abc"],
11847        is_incomplete,
11848        counter.clone(),
11849        &mut cx,
11850    )
11851    .await;
11852    cx.run_until_parked();
11853    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11854    cx.assert_editor_state("obj.aˇb");
11855    check_displayed_completions(vec!["a", "ab", "abc"], &mut cx);
11856
11857    // Backspace to delete "a" - dismisses menu.
11858    cx.update_editor(|editor, window, cx| {
11859        editor.backspace(&Backspace, window, cx);
11860    });
11861    cx.run_until_parked();
11862    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11863    cx.assert_editor_state("obj.ˇb");
11864    cx.update_editor(|editor, _, _| {
11865        assert_eq!(editor.context_menu_visible(), false);
11866    });
11867}
11868
11869#[gpui::test]
11870async fn test_word_completion(cx: &mut TestAppContext) {
11871    let lsp_fetch_timeout_ms = 10;
11872    init_test(cx, |language_settings| {
11873        language_settings.defaults.completions = Some(CompletionSettings {
11874            words: WordsCompletionMode::Fallback,
11875            lsp: true,
11876            lsp_fetch_timeout_ms: 10,
11877            lsp_insert_mode: LspInsertMode::Insert,
11878        });
11879    });
11880
11881    let mut cx = EditorLspTestContext::new_rust(
11882        lsp::ServerCapabilities {
11883            completion_provider: Some(lsp::CompletionOptions {
11884                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11885                ..lsp::CompletionOptions::default()
11886            }),
11887            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11888            ..lsp::ServerCapabilities::default()
11889        },
11890        cx,
11891    )
11892    .await;
11893
11894    let throttle_completions = Arc::new(AtomicBool::new(false));
11895
11896    let lsp_throttle_completions = throttle_completions.clone();
11897    let _completion_requests_handler =
11898        cx.lsp
11899            .server
11900            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11901                let lsp_throttle_completions = lsp_throttle_completions.clone();
11902                let cx = cx.clone();
11903                async move {
11904                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11905                        cx.background_executor()
11906                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11907                            .await;
11908                    }
11909                    Ok(Some(lsp::CompletionResponse::Array(vec![
11910                        lsp::CompletionItem {
11911                            label: "first".into(),
11912                            ..lsp::CompletionItem::default()
11913                        },
11914                        lsp::CompletionItem {
11915                            label: "last".into(),
11916                            ..lsp::CompletionItem::default()
11917                        },
11918                    ])))
11919                }
11920            });
11921
11922    cx.set_state(indoc! {"
11923        oneˇ
11924        two
11925        three
11926    "});
11927    cx.simulate_keystroke(".");
11928    cx.executor().run_until_parked();
11929    cx.condition(|editor, _| editor.context_menu_visible())
11930        .await;
11931    cx.update_editor(|editor, window, cx| {
11932        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11933        {
11934            assert_eq!(
11935                completion_menu_entries(&menu),
11936                &["first", "last"],
11937                "When LSP server is fast to reply, no fallback word completions are used"
11938            );
11939        } else {
11940            panic!("expected completion menu to be open");
11941        }
11942        editor.cancel(&Cancel, window, cx);
11943    });
11944    cx.executor().run_until_parked();
11945    cx.condition(|editor, _| !editor.context_menu_visible())
11946        .await;
11947
11948    throttle_completions.store(true, atomic::Ordering::Release);
11949    cx.simulate_keystroke(".");
11950    cx.executor()
11951        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11952    cx.executor().run_until_parked();
11953    cx.condition(|editor, _| editor.context_menu_visible())
11954        .await;
11955    cx.update_editor(|editor, _, _| {
11956        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11957        {
11958            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11959                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11960        } else {
11961            panic!("expected completion menu to be open");
11962        }
11963    });
11964}
11965
11966#[gpui::test]
11967async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11968    init_test(cx, |language_settings| {
11969        language_settings.defaults.completions = Some(CompletionSettings {
11970            words: WordsCompletionMode::Enabled,
11971            lsp: true,
11972            lsp_fetch_timeout_ms: 0,
11973            lsp_insert_mode: LspInsertMode::Insert,
11974        });
11975    });
11976
11977    let mut cx = EditorLspTestContext::new_rust(
11978        lsp::ServerCapabilities {
11979            completion_provider: Some(lsp::CompletionOptions {
11980                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11981                ..lsp::CompletionOptions::default()
11982            }),
11983            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11984            ..lsp::ServerCapabilities::default()
11985        },
11986        cx,
11987    )
11988    .await;
11989
11990    let _completion_requests_handler =
11991        cx.lsp
11992            .server
11993            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11994                Ok(Some(lsp::CompletionResponse::Array(vec![
11995                    lsp::CompletionItem {
11996                        label: "first".into(),
11997                        ..lsp::CompletionItem::default()
11998                    },
11999                    lsp::CompletionItem {
12000                        label: "last".into(),
12001                        ..lsp::CompletionItem::default()
12002                    },
12003                ])))
12004            });
12005
12006    cx.set_state(indoc! {"ˇ
12007        first
12008        last
12009        second
12010    "});
12011    cx.simulate_keystroke(".");
12012    cx.executor().run_until_parked();
12013    cx.condition(|editor, _| editor.context_menu_visible())
12014        .await;
12015    cx.update_editor(|editor, _, _| {
12016        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12017        {
12018            assert_eq!(
12019                completion_menu_entries(&menu),
12020                &["first", "last", "second"],
12021                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
12022            );
12023        } else {
12024            panic!("expected completion menu to be open");
12025        }
12026    });
12027}
12028
12029#[gpui::test]
12030async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
12031    init_test(cx, |language_settings| {
12032        language_settings.defaults.completions = Some(CompletionSettings {
12033            words: WordsCompletionMode::Disabled,
12034            lsp: true,
12035            lsp_fetch_timeout_ms: 0,
12036            lsp_insert_mode: LspInsertMode::Insert,
12037        });
12038    });
12039
12040    let mut cx = EditorLspTestContext::new_rust(
12041        lsp::ServerCapabilities {
12042            completion_provider: Some(lsp::CompletionOptions {
12043                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
12044                ..lsp::CompletionOptions::default()
12045            }),
12046            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
12047            ..lsp::ServerCapabilities::default()
12048        },
12049        cx,
12050    )
12051    .await;
12052
12053    let _completion_requests_handler =
12054        cx.lsp
12055            .server
12056            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12057                panic!("LSP completions should not be queried when dealing with word completions")
12058            });
12059
12060    cx.set_state(indoc! {"ˇ
12061        first
12062        last
12063        second
12064    "});
12065    cx.update_editor(|editor, window, cx| {
12066        editor.show_word_completions(&ShowWordCompletions, window, cx);
12067    });
12068    cx.executor().run_until_parked();
12069    cx.condition(|editor, _| editor.context_menu_visible())
12070        .await;
12071    cx.update_editor(|editor, _, _| {
12072        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12073        {
12074            assert_eq!(
12075                completion_menu_entries(&menu),
12076                &["first", "last", "second"],
12077                "`ShowWordCompletions` action should show word completions"
12078            );
12079        } else {
12080            panic!("expected completion menu to be open");
12081        }
12082    });
12083
12084    cx.simulate_keystroke("l");
12085    cx.executor().run_until_parked();
12086    cx.condition(|editor, _| editor.context_menu_visible())
12087        .await;
12088    cx.update_editor(|editor, _, _| {
12089        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12090        {
12091            assert_eq!(
12092                completion_menu_entries(&menu),
12093                &["last"],
12094                "After showing word completions, further editing should filter them and not query the LSP"
12095            );
12096        } else {
12097            panic!("expected completion menu to be open");
12098        }
12099    });
12100}
12101
12102#[gpui::test]
12103async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
12104    init_test(cx, |language_settings| {
12105        language_settings.defaults.completions = Some(CompletionSettings {
12106            words: WordsCompletionMode::Fallback,
12107            lsp: false,
12108            lsp_fetch_timeout_ms: 0,
12109            lsp_insert_mode: LspInsertMode::Insert,
12110        });
12111    });
12112
12113    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12114
12115    cx.set_state(indoc! {"ˇ
12116        0_usize
12117        let
12118        33
12119        4.5f32
12120    "});
12121    cx.update_editor(|editor, window, cx| {
12122        editor.show_completions(&ShowCompletions::default(), window, cx);
12123    });
12124    cx.executor().run_until_parked();
12125    cx.condition(|editor, _| editor.context_menu_visible())
12126        .await;
12127    cx.update_editor(|editor, window, cx| {
12128        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12129        {
12130            assert_eq!(
12131                completion_menu_entries(&menu),
12132                &["let"],
12133                "With no digits in the completion query, no digits should be in the word completions"
12134            );
12135        } else {
12136            panic!("expected completion menu to be open");
12137        }
12138        editor.cancel(&Cancel, window, cx);
12139    });
12140
12141    cx.set_state(indoc! {"12142        0_usize
12143        let
12144        3
12145        33.35f32
12146    "});
12147    cx.update_editor(|editor, window, cx| {
12148        editor.show_completions(&ShowCompletions::default(), window, cx);
12149    });
12150    cx.executor().run_until_parked();
12151    cx.condition(|editor, _| editor.context_menu_visible())
12152        .await;
12153    cx.update_editor(|editor, _, _| {
12154        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12155        {
12156            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
12157                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
12158        } else {
12159            panic!("expected completion menu to be open");
12160        }
12161    });
12162}
12163
12164fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
12165    let position = || lsp::Position {
12166        line: params.text_document_position.position.line,
12167        character: params.text_document_position.position.character,
12168    };
12169    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12170        range: lsp::Range {
12171            start: position(),
12172            end: position(),
12173        },
12174        new_text: text.to_string(),
12175    }))
12176}
12177
12178#[gpui::test]
12179async fn test_multiline_completion(cx: &mut TestAppContext) {
12180    init_test(cx, |_| {});
12181
12182    let fs = FakeFs::new(cx.executor());
12183    fs.insert_tree(
12184        path!("/a"),
12185        json!({
12186            "main.ts": "a",
12187        }),
12188    )
12189    .await;
12190
12191    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12192    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12193    let typescript_language = Arc::new(Language::new(
12194        LanguageConfig {
12195            name: "TypeScript".into(),
12196            matcher: LanguageMatcher {
12197                path_suffixes: vec!["ts".to_string()],
12198                ..LanguageMatcher::default()
12199            },
12200            line_comments: vec!["// ".into()],
12201            ..LanguageConfig::default()
12202        },
12203        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12204    ));
12205    language_registry.add(typescript_language.clone());
12206    let mut fake_servers = language_registry.register_fake_lsp(
12207        "TypeScript",
12208        FakeLspAdapter {
12209            capabilities: lsp::ServerCapabilities {
12210                completion_provider: Some(lsp::CompletionOptions {
12211                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
12212                    ..lsp::CompletionOptions::default()
12213                }),
12214                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
12215                ..lsp::ServerCapabilities::default()
12216            },
12217            // Emulate vtsls label generation
12218            label_for_completion: Some(Box::new(|item, _| {
12219                let text = if let Some(description) = item
12220                    .label_details
12221                    .as_ref()
12222                    .and_then(|label_details| label_details.description.as_ref())
12223                {
12224                    format!("{} {}", item.label, description)
12225                } else if let Some(detail) = &item.detail {
12226                    format!("{} {}", item.label, detail)
12227                } else {
12228                    item.label.clone()
12229                };
12230                let len = text.len();
12231                Some(language::CodeLabel {
12232                    text,
12233                    runs: Vec::new(),
12234                    filter_range: 0..len,
12235                })
12236            })),
12237            ..FakeLspAdapter::default()
12238        },
12239    );
12240    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12241    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12242    let worktree_id = workspace
12243        .update(cx, |workspace, _window, cx| {
12244            workspace.project().update(cx, |project, cx| {
12245                project.worktrees(cx).next().unwrap().read(cx).id()
12246            })
12247        })
12248        .unwrap();
12249    let _buffer = project
12250        .update(cx, |project, cx| {
12251            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
12252        })
12253        .await
12254        .unwrap();
12255    let editor = workspace
12256        .update(cx, |workspace, window, cx| {
12257            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
12258        })
12259        .unwrap()
12260        .await
12261        .unwrap()
12262        .downcast::<Editor>()
12263        .unwrap();
12264    let fake_server = fake_servers.next().await.unwrap();
12265
12266    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
12267    let multiline_label_2 = "a\nb\nc\n";
12268    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
12269    let multiline_description = "d\ne\nf\n";
12270    let multiline_detail_2 = "g\nh\ni\n";
12271
12272    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
12273        move |params, _| async move {
12274            Ok(Some(lsp::CompletionResponse::Array(vec![
12275                lsp::CompletionItem {
12276                    label: multiline_label.to_string(),
12277                    text_edit: gen_text_edit(&params, "new_text_1"),
12278                    ..lsp::CompletionItem::default()
12279                },
12280                lsp::CompletionItem {
12281                    label: "single line label 1".to_string(),
12282                    detail: Some(multiline_detail.to_string()),
12283                    text_edit: gen_text_edit(&params, "new_text_2"),
12284                    ..lsp::CompletionItem::default()
12285                },
12286                lsp::CompletionItem {
12287                    label: "single line label 2".to_string(),
12288                    label_details: Some(lsp::CompletionItemLabelDetails {
12289                        description: Some(multiline_description.to_string()),
12290                        detail: None,
12291                    }),
12292                    text_edit: gen_text_edit(&params, "new_text_2"),
12293                    ..lsp::CompletionItem::default()
12294                },
12295                lsp::CompletionItem {
12296                    label: multiline_label_2.to_string(),
12297                    detail: Some(multiline_detail_2.to_string()),
12298                    text_edit: gen_text_edit(&params, "new_text_3"),
12299                    ..lsp::CompletionItem::default()
12300                },
12301                lsp::CompletionItem {
12302                    label: "Label with many     spaces and \t but without newlines".to_string(),
12303                    detail: Some(
12304                        "Details with many     spaces and \t but without newlines".to_string(),
12305                    ),
12306                    text_edit: gen_text_edit(&params, "new_text_4"),
12307                    ..lsp::CompletionItem::default()
12308                },
12309            ])))
12310        },
12311    );
12312
12313    editor.update_in(cx, |editor, window, cx| {
12314        cx.focus_self(window);
12315        editor.move_to_end(&MoveToEnd, window, cx);
12316        editor.handle_input(".", window, cx);
12317    });
12318    cx.run_until_parked();
12319    completion_handle.next().await.unwrap();
12320
12321    editor.update(cx, |editor, _| {
12322        assert!(editor.context_menu_visible());
12323        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12324        {
12325            let completion_labels = menu
12326                .completions
12327                .borrow()
12328                .iter()
12329                .map(|c| c.label.text.clone())
12330                .collect::<Vec<_>>();
12331            assert_eq!(
12332                completion_labels,
12333                &[
12334                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
12335                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
12336                    "single line label 2 d e f ",
12337                    "a b c g h i ",
12338                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
12339                ],
12340                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
12341            );
12342
12343            for completion in menu
12344                .completions
12345                .borrow()
12346                .iter() {
12347                    assert_eq!(
12348                        completion.label.filter_range,
12349                        0..completion.label.text.len(),
12350                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
12351                    );
12352                }
12353        } else {
12354            panic!("expected completion menu to be open");
12355        }
12356    });
12357}
12358
12359#[gpui::test]
12360async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
12361    init_test(cx, |_| {});
12362    let mut cx = EditorLspTestContext::new_rust(
12363        lsp::ServerCapabilities {
12364            completion_provider: Some(lsp::CompletionOptions {
12365                trigger_characters: Some(vec![".".to_string()]),
12366                ..Default::default()
12367            }),
12368            ..Default::default()
12369        },
12370        cx,
12371    )
12372    .await;
12373    cx.lsp
12374        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
12375            Ok(Some(lsp::CompletionResponse::Array(vec![
12376                lsp::CompletionItem {
12377                    label: "first".into(),
12378                    ..Default::default()
12379                },
12380                lsp::CompletionItem {
12381                    label: "last".into(),
12382                    ..Default::default()
12383                },
12384            ])))
12385        });
12386    cx.set_state("variableˇ");
12387    cx.simulate_keystroke(".");
12388    cx.executor().run_until_parked();
12389
12390    cx.update_editor(|editor, _, _| {
12391        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12392        {
12393            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
12394        } else {
12395            panic!("expected completion menu to be open");
12396        }
12397    });
12398
12399    cx.update_editor(|editor, window, cx| {
12400        editor.move_page_down(&MovePageDown::default(), window, cx);
12401        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12402        {
12403            assert!(
12404                menu.selected_item == 1,
12405                "expected PageDown to select the last item from the context menu"
12406            );
12407        } else {
12408            panic!("expected completion menu to stay open after PageDown");
12409        }
12410    });
12411
12412    cx.update_editor(|editor, window, cx| {
12413        editor.move_page_up(&MovePageUp::default(), window, cx);
12414        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12415        {
12416            assert!(
12417                menu.selected_item == 0,
12418                "expected PageUp to select the first item from the context menu"
12419            );
12420        } else {
12421            panic!("expected completion menu to stay open after PageUp");
12422        }
12423    });
12424}
12425
12426#[gpui::test]
12427async fn test_as_is_completions(cx: &mut TestAppContext) {
12428    init_test(cx, |_| {});
12429    let mut cx = EditorLspTestContext::new_rust(
12430        lsp::ServerCapabilities {
12431            completion_provider: Some(lsp::CompletionOptions {
12432                ..Default::default()
12433            }),
12434            ..Default::default()
12435        },
12436        cx,
12437    )
12438    .await;
12439    cx.lsp
12440        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
12441            Ok(Some(lsp::CompletionResponse::Array(vec![
12442                lsp::CompletionItem {
12443                    label: "unsafe".into(),
12444                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12445                        range: lsp::Range {
12446                            start: lsp::Position {
12447                                line: 1,
12448                                character: 2,
12449                            },
12450                            end: lsp::Position {
12451                                line: 1,
12452                                character: 3,
12453                            },
12454                        },
12455                        new_text: "unsafe".to_string(),
12456                    })),
12457                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
12458                    ..Default::default()
12459                },
12460            ])))
12461        });
12462    cx.set_state("fn a() {}\n");
12463    cx.executor().run_until_parked();
12464    cx.update_editor(|editor, window, cx| {
12465        editor.show_completions(
12466            &ShowCompletions {
12467                trigger: Some("\n".into()),
12468            },
12469            window,
12470            cx,
12471        );
12472    });
12473    cx.executor().run_until_parked();
12474
12475    cx.update_editor(|editor, window, cx| {
12476        editor.confirm_completion(&Default::default(), window, cx)
12477    });
12478    cx.executor().run_until_parked();
12479    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
12480}
12481
12482#[gpui::test]
12483async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
12484    init_test(cx, |_| {});
12485
12486    let mut cx = EditorLspTestContext::new_rust(
12487        lsp::ServerCapabilities {
12488            completion_provider: Some(lsp::CompletionOptions {
12489                trigger_characters: Some(vec![".".to_string()]),
12490                resolve_provider: Some(true),
12491                ..Default::default()
12492            }),
12493            ..Default::default()
12494        },
12495        cx,
12496    )
12497    .await;
12498
12499    cx.set_state("fn main() { let a = 2ˇ; }");
12500    cx.simulate_keystroke(".");
12501    let completion_item = lsp::CompletionItem {
12502        label: "Some".into(),
12503        kind: Some(lsp::CompletionItemKind::SNIPPET),
12504        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12505        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12506            kind: lsp::MarkupKind::Markdown,
12507            value: "```rust\nSome(2)\n```".to_string(),
12508        })),
12509        deprecated: Some(false),
12510        sort_text: Some("Some".to_string()),
12511        filter_text: Some("Some".to_string()),
12512        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12513        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12514            range: lsp::Range {
12515                start: lsp::Position {
12516                    line: 0,
12517                    character: 22,
12518                },
12519                end: lsp::Position {
12520                    line: 0,
12521                    character: 22,
12522                },
12523            },
12524            new_text: "Some(2)".to_string(),
12525        })),
12526        additional_text_edits: Some(vec![lsp::TextEdit {
12527            range: lsp::Range {
12528                start: lsp::Position {
12529                    line: 0,
12530                    character: 20,
12531                },
12532                end: lsp::Position {
12533                    line: 0,
12534                    character: 22,
12535                },
12536            },
12537            new_text: "".to_string(),
12538        }]),
12539        ..Default::default()
12540    };
12541
12542    let closure_completion_item = completion_item.clone();
12543    let counter = Arc::new(AtomicUsize::new(0));
12544    let counter_clone = counter.clone();
12545    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12546        let task_completion_item = closure_completion_item.clone();
12547        counter_clone.fetch_add(1, atomic::Ordering::Release);
12548        async move {
12549            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12550                is_incomplete: true,
12551                item_defaults: None,
12552                items: vec![task_completion_item],
12553            })))
12554        }
12555    });
12556
12557    cx.condition(|editor, _| editor.context_menu_visible())
12558        .await;
12559    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
12560    assert!(request.next().await.is_some());
12561    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
12562
12563    cx.simulate_keystrokes("S o m");
12564    cx.condition(|editor, _| editor.context_menu_visible())
12565        .await;
12566    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
12567    assert!(request.next().await.is_some());
12568    assert!(request.next().await.is_some());
12569    assert!(request.next().await.is_some());
12570    request.close();
12571    assert!(request.next().await.is_none());
12572    assert_eq!(
12573        counter.load(atomic::Ordering::Acquire),
12574        4,
12575        "With the completions menu open, only one LSP request should happen per input"
12576    );
12577}
12578
12579#[gpui::test]
12580async fn test_toggle_comment(cx: &mut TestAppContext) {
12581    init_test(cx, |_| {});
12582    let mut cx = EditorTestContext::new(cx).await;
12583    let language = Arc::new(Language::new(
12584        LanguageConfig {
12585            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12586            ..Default::default()
12587        },
12588        Some(tree_sitter_rust::LANGUAGE.into()),
12589    ));
12590    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12591
12592    // If multiple selections intersect a line, the line is only toggled once.
12593    cx.set_state(indoc! {"
12594        fn a() {
12595            «//b();
12596            ˇ»// «c();
12597            //ˇ»  d();
12598        }
12599    "});
12600
12601    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12602
12603    cx.assert_editor_state(indoc! {"
12604        fn a() {
12605            «b();
12606            c();
12607            ˇ» d();
12608        }
12609    "});
12610
12611    // The comment prefix is inserted at the same column for every line in a
12612    // selection.
12613    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12614
12615    cx.assert_editor_state(indoc! {"
12616        fn a() {
12617            // «b();
12618            // c();
12619            ˇ»//  d();
12620        }
12621    "});
12622
12623    // If a selection ends at the beginning of a line, that line is not toggled.
12624    cx.set_selections_state(indoc! {"
12625        fn a() {
12626            // b();
12627            «// c();
12628        ˇ»    //  d();
12629        }
12630    "});
12631
12632    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12633
12634    cx.assert_editor_state(indoc! {"
12635        fn a() {
12636            // b();
12637            «c();
12638        ˇ»    //  d();
12639        }
12640    "});
12641
12642    // If a selection span a single line and is empty, the line is toggled.
12643    cx.set_state(indoc! {"
12644        fn a() {
12645            a();
12646            b();
12647        ˇ
12648        }
12649    "});
12650
12651    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12652
12653    cx.assert_editor_state(indoc! {"
12654        fn a() {
12655            a();
12656            b();
12657        //•ˇ
12658        }
12659    "});
12660
12661    // If a selection span multiple lines, empty lines are not toggled.
12662    cx.set_state(indoc! {"
12663        fn a() {
12664            «a();
12665
12666            c();ˇ»
12667        }
12668    "});
12669
12670    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12671
12672    cx.assert_editor_state(indoc! {"
12673        fn a() {
12674            // «a();
12675
12676            // c();ˇ»
12677        }
12678    "});
12679
12680    // If a selection includes multiple comment prefixes, all lines are uncommented.
12681    cx.set_state(indoc! {"
12682        fn a() {
12683            «// a();
12684            /// b();
12685            //! c();ˇ»
12686        }
12687    "});
12688
12689    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12690
12691    cx.assert_editor_state(indoc! {"
12692        fn a() {
12693            «a();
12694            b();
12695            c();ˇ»
12696        }
12697    "});
12698}
12699
12700#[gpui::test]
12701async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
12702    init_test(cx, |_| {});
12703    let mut cx = EditorTestContext::new(cx).await;
12704    let language = Arc::new(Language::new(
12705        LanguageConfig {
12706            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12707            ..Default::default()
12708        },
12709        Some(tree_sitter_rust::LANGUAGE.into()),
12710    ));
12711    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12712
12713    let toggle_comments = &ToggleComments {
12714        advance_downwards: false,
12715        ignore_indent: true,
12716    };
12717
12718    // If multiple selections intersect a line, the line is only toggled once.
12719    cx.set_state(indoc! {"
12720        fn a() {
12721        //    «b();
12722        //    c();
12723        //    ˇ» d();
12724        }
12725    "});
12726
12727    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12728
12729    cx.assert_editor_state(indoc! {"
12730        fn a() {
12731            «b();
12732            c();
12733            ˇ» d();
12734        }
12735    "});
12736
12737    // The comment prefix is inserted at the beginning of each line
12738    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12739
12740    cx.assert_editor_state(indoc! {"
12741        fn a() {
12742        //    «b();
12743        //    c();
12744        //    ˇ» d();
12745        }
12746    "});
12747
12748    // If a selection ends at the beginning of a line, that line is not toggled.
12749    cx.set_selections_state(indoc! {"
12750        fn a() {
12751        //    b();
12752        //    «c();
12753        ˇ»//     d();
12754        }
12755    "});
12756
12757    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12758
12759    cx.assert_editor_state(indoc! {"
12760        fn a() {
12761        //    b();
12762            «c();
12763        ˇ»//     d();
12764        }
12765    "});
12766
12767    // If a selection span a single line and is empty, the line is toggled.
12768    cx.set_state(indoc! {"
12769        fn a() {
12770            a();
12771            b();
12772        ˇ
12773        }
12774    "});
12775
12776    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12777
12778    cx.assert_editor_state(indoc! {"
12779        fn a() {
12780            a();
12781            b();
12782        //ˇ
12783        }
12784    "});
12785
12786    // If a selection span multiple lines, empty lines are not toggled.
12787    cx.set_state(indoc! {"
12788        fn a() {
12789            «a();
12790
12791            c();ˇ»
12792        }
12793    "});
12794
12795    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12796
12797    cx.assert_editor_state(indoc! {"
12798        fn a() {
12799        //    «a();
12800
12801        //    c();ˇ»
12802        }
12803    "});
12804
12805    // If a selection includes multiple comment prefixes, all lines are uncommented.
12806    cx.set_state(indoc! {"
12807        fn a() {
12808        //    «a();
12809        ///    b();
12810        //!    c();ˇ»
12811        }
12812    "});
12813
12814    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12815
12816    cx.assert_editor_state(indoc! {"
12817        fn a() {
12818            «a();
12819            b();
12820            c();ˇ»
12821        }
12822    "});
12823}
12824
12825#[gpui::test]
12826async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12827    init_test(cx, |_| {});
12828
12829    let language = Arc::new(Language::new(
12830        LanguageConfig {
12831            line_comments: vec!["// ".into()],
12832            ..Default::default()
12833        },
12834        Some(tree_sitter_rust::LANGUAGE.into()),
12835    ));
12836
12837    let mut cx = EditorTestContext::new(cx).await;
12838
12839    cx.language_registry().add(language.clone());
12840    cx.update_buffer(|buffer, cx| {
12841        buffer.set_language(Some(language), cx);
12842    });
12843
12844    let toggle_comments = &ToggleComments {
12845        advance_downwards: true,
12846        ignore_indent: false,
12847    };
12848
12849    // Single cursor on one line -> advance
12850    // Cursor moves horizontally 3 characters as well on non-blank line
12851    cx.set_state(indoc!(
12852        "fn a() {
12853             ˇdog();
12854             cat();
12855        }"
12856    ));
12857    cx.update_editor(|editor, window, cx| {
12858        editor.toggle_comments(toggle_comments, window, cx);
12859    });
12860    cx.assert_editor_state(indoc!(
12861        "fn a() {
12862             // dog();
12863             catˇ();
12864        }"
12865    ));
12866
12867    // Single selection on one line -> don't advance
12868    cx.set_state(indoc!(
12869        "fn a() {
12870             «dog()ˇ»;
12871             cat();
12872        }"
12873    ));
12874    cx.update_editor(|editor, window, cx| {
12875        editor.toggle_comments(toggle_comments, window, cx);
12876    });
12877    cx.assert_editor_state(indoc!(
12878        "fn a() {
12879             // «dog()ˇ»;
12880             cat();
12881        }"
12882    ));
12883
12884    // Multiple cursors on one line -> advance
12885    cx.set_state(indoc!(
12886        "fn a() {
12887             ˇdˇog();
12888             cat();
12889        }"
12890    ));
12891    cx.update_editor(|editor, window, cx| {
12892        editor.toggle_comments(toggle_comments, window, cx);
12893    });
12894    cx.assert_editor_state(indoc!(
12895        "fn a() {
12896             // dog();
12897             catˇ(ˇ);
12898        }"
12899    ));
12900
12901    // Multiple cursors on one line, with selection -> don't advance
12902    cx.set_state(indoc!(
12903        "fn a() {
12904             ˇdˇog«()ˇ»;
12905             cat();
12906        }"
12907    ));
12908    cx.update_editor(|editor, window, cx| {
12909        editor.toggle_comments(toggle_comments, window, cx);
12910    });
12911    cx.assert_editor_state(indoc!(
12912        "fn a() {
12913             // ˇdˇog«()ˇ»;
12914             cat();
12915        }"
12916    ));
12917
12918    // Single cursor on one line -> advance
12919    // Cursor moves to column 0 on blank line
12920    cx.set_state(indoc!(
12921        "fn a() {
12922             ˇdog();
12923
12924             cat();
12925        }"
12926    ));
12927    cx.update_editor(|editor, window, cx| {
12928        editor.toggle_comments(toggle_comments, window, cx);
12929    });
12930    cx.assert_editor_state(indoc!(
12931        "fn a() {
12932             // dog();
12933        ˇ
12934             cat();
12935        }"
12936    ));
12937
12938    // Single cursor on one line -> advance
12939    // Cursor starts and ends at column 0
12940    cx.set_state(indoc!(
12941        "fn a() {
12942         ˇ    dog();
12943             cat();
12944        }"
12945    ));
12946    cx.update_editor(|editor, window, cx| {
12947        editor.toggle_comments(toggle_comments, window, cx);
12948    });
12949    cx.assert_editor_state(indoc!(
12950        "fn a() {
12951             // dog();
12952         ˇ    cat();
12953        }"
12954    ));
12955}
12956
12957#[gpui::test]
12958async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12959    init_test(cx, |_| {});
12960
12961    let mut cx = EditorTestContext::new(cx).await;
12962
12963    let html_language = Arc::new(
12964        Language::new(
12965            LanguageConfig {
12966                name: "HTML".into(),
12967                block_comment: Some(("<!-- ".into(), " -->".into())),
12968                ..Default::default()
12969            },
12970            Some(tree_sitter_html::LANGUAGE.into()),
12971        )
12972        .with_injection_query(
12973            r#"
12974            (script_element
12975                (raw_text) @injection.content
12976                (#set! injection.language "javascript"))
12977            "#,
12978        )
12979        .unwrap(),
12980    );
12981
12982    let javascript_language = Arc::new(Language::new(
12983        LanguageConfig {
12984            name: "JavaScript".into(),
12985            line_comments: vec!["// ".into()],
12986            ..Default::default()
12987        },
12988        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12989    ));
12990
12991    cx.language_registry().add(html_language.clone());
12992    cx.language_registry().add(javascript_language.clone());
12993    cx.update_buffer(|buffer, cx| {
12994        buffer.set_language(Some(html_language), cx);
12995    });
12996
12997    // Toggle comments for empty selections
12998    cx.set_state(
12999        &r#"
13000            <p>A</p>ˇ
13001            <p>B</p>ˇ
13002            <p>C</p>ˇ
13003        "#
13004        .unindent(),
13005    );
13006    cx.update_editor(|editor, window, cx| {
13007        editor.toggle_comments(&ToggleComments::default(), window, cx)
13008    });
13009    cx.assert_editor_state(
13010        &r#"
13011            <!-- <p>A</p>ˇ -->
13012            <!-- <p>B</p>ˇ -->
13013            <!-- <p>C</p>ˇ -->
13014        "#
13015        .unindent(),
13016    );
13017    cx.update_editor(|editor, window, cx| {
13018        editor.toggle_comments(&ToggleComments::default(), window, cx)
13019    });
13020    cx.assert_editor_state(
13021        &r#"
13022            <p>A</p>ˇ
13023            <p>B</p>ˇ
13024            <p>C</p>ˇ
13025        "#
13026        .unindent(),
13027    );
13028
13029    // Toggle comments for mixture of empty and non-empty selections, where
13030    // multiple selections occupy a given line.
13031    cx.set_state(
13032        &r#"
13033            <p>A«</p>
13034            <p>ˇ»B</p>ˇ
13035            <p>C«</p>
13036            <p>ˇ»D</p>ˇ
13037        "#
13038        .unindent(),
13039    );
13040
13041    cx.update_editor(|editor, window, cx| {
13042        editor.toggle_comments(&ToggleComments::default(), window, cx)
13043    });
13044    cx.assert_editor_state(
13045        &r#"
13046            <!-- <p>A«</p>
13047            <p>ˇ»B</p>ˇ -->
13048            <!-- <p>C«</p>
13049            <p>ˇ»D</p>ˇ -->
13050        "#
13051        .unindent(),
13052    );
13053    cx.update_editor(|editor, window, cx| {
13054        editor.toggle_comments(&ToggleComments::default(), window, cx)
13055    });
13056    cx.assert_editor_state(
13057        &r#"
13058            <p>A«</p>
13059            <p>ˇ»B</p>ˇ
13060            <p>C«</p>
13061            <p>ˇ»D</p>ˇ
13062        "#
13063        .unindent(),
13064    );
13065
13066    // Toggle comments when different languages are active for different
13067    // selections.
13068    cx.set_state(
13069        &r#"
13070            ˇ<script>
13071                ˇvar x = new Y();
13072            ˇ</script>
13073        "#
13074        .unindent(),
13075    );
13076    cx.executor().run_until_parked();
13077    cx.update_editor(|editor, window, cx| {
13078        editor.toggle_comments(&ToggleComments::default(), window, cx)
13079    });
13080    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
13081    // Uncommenting and commenting from this position brings in even more wrong artifacts.
13082    cx.assert_editor_state(
13083        &r#"
13084            <!-- ˇ<script> -->
13085                // ˇvar x = new Y();
13086            <!-- ˇ</script> -->
13087        "#
13088        .unindent(),
13089    );
13090}
13091
13092#[gpui::test]
13093fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
13094    init_test(cx, |_| {});
13095
13096    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
13097    let multibuffer = cx.new(|cx| {
13098        let mut multibuffer = MultiBuffer::new(ReadWrite);
13099        multibuffer.push_excerpts(
13100            buffer.clone(),
13101            [
13102                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
13103                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
13104            ],
13105            cx,
13106        );
13107        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
13108        multibuffer
13109    });
13110
13111    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
13112    editor.update_in(cx, |editor, window, cx| {
13113        assert_eq!(editor.text(cx), "aaaa\nbbbb");
13114        editor.change_selections(None, window, cx, |s| {
13115            s.select_ranges([
13116                Point::new(0, 0)..Point::new(0, 0),
13117                Point::new(1, 0)..Point::new(1, 0),
13118            ])
13119        });
13120
13121        editor.handle_input("X", window, cx);
13122        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
13123        assert_eq!(
13124            editor.selections.ranges(cx),
13125            [
13126                Point::new(0, 1)..Point::new(0, 1),
13127                Point::new(1, 1)..Point::new(1, 1),
13128            ]
13129        );
13130
13131        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
13132        editor.change_selections(None, window, cx, |s| {
13133            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
13134        });
13135        editor.backspace(&Default::default(), window, cx);
13136        assert_eq!(editor.text(cx), "Xa\nbbb");
13137        assert_eq!(
13138            editor.selections.ranges(cx),
13139            [Point::new(1, 0)..Point::new(1, 0)]
13140        );
13141
13142        editor.change_selections(None, window, cx, |s| {
13143            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
13144        });
13145        editor.backspace(&Default::default(), window, cx);
13146        assert_eq!(editor.text(cx), "X\nbb");
13147        assert_eq!(
13148            editor.selections.ranges(cx),
13149            [Point::new(0, 1)..Point::new(0, 1)]
13150        );
13151    });
13152}
13153
13154#[gpui::test]
13155fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
13156    init_test(cx, |_| {});
13157
13158    let markers = vec![('[', ']').into(), ('(', ')').into()];
13159    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
13160        indoc! {"
13161            [aaaa
13162            (bbbb]
13163            cccc)",
13164        },
13165        markers.clone(),
13166    );
13167    let excerpt_ranges = markers.into_iter().map(|marker| {
13168        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
13169        ExcerptRange::new(context.clone())
13170    });
13171    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
13172    let multibuffer = cx.new(|cx| {
13173        let mut multibuffer = MultiBuffer::new(ReadWrite);
13174        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
13175        multibuffer
13176    });
13177
13178    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
13179    editor.update_in(cx, |editor, window, cx| {
13180        let (expected_text, selection_ranges) = marked_text_ranges(
13181            indoc! {"
13182                aaaa
13183                bˇbbb
13184                bˇbbˇb
13185                cccc"
13186            },
13187            true,
13188        );
13189        assert_eq!(editor.text(cx), expected_text);
13190        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
13191
13192        editor.handle_input("X", window, cx);
13193
13194        let (expected_text, expected_selections) = marked_text_ranges(
13195            indoc! {"
13196                aaaa
13197                bXˇbbXb
13198                bXˇbbXˇb
13199                cccc"
13200            },
13201            false,
13202        );
13203        assert_eq!(editor.text(cx), expected_text);
13204        assert_eq!(editor.selections.ranges(cx), expected_selections);
13205
13206        editor.newline(&Newline, window, cx);
13207        let (expected_text, expected_selections) = marked_text_ranges(
13208            indoc! {"
13209                aaaa
13210                bX
13211                ˇbbX
13212                b
13213                bX
13214                ˇbbX
13215                ˇb
13216                cccc"
13217            },
13218            false,
13219        );
13220        assert_eq!(editor.text(cx), expected_text);
13221        assert_eq!(editor.selections.ranges(cx), expected_selections);
13222    });
13223}
13224
13225#[gpui::test]
13226fn test_refresh_selections(cx: &mut TestAppContext) {
13227    init_test(cx, |_| {});
13228
13229    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
13230    let mut excerpt1_id = None;
13231    let multibuffer = cx.new(|cx| {
13232        let mut multibuffer = MultiBuffer::new(ReadWrite);
13233        excerpt1_id = multibuffer
13234            .push_excerpts(
13235                buffer.clone(),
13236                [
13237                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
13238                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
13239                ],
13240                cx,
13241            )
13242            .into_iter()
13243            .next();
13244        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
13245        multibuffer
13246    });
13247
13248    let editor = cx.add_window(|window, cx| {
13249        let mut editor = build_editor(multibuffer.clone(), window, cx);
13250        let snapshot = editor.snapshot(window, cx);
13251        editor.change_selections(None, window, cx, |s| {
13252            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
13253        });
13254        editor.begin_selection(
13255            Point::new(2, 1).to_display_point(&snapshot),
13256            true,
13257            1,
13258            window,
13259            cx,
13260        );
13261        assert_eq!(
13262            editor.selections.ranges(cx),
13263            [
13264                Point::new(1, 3)..Point::new(1, 3),
13265                Point::new(2, 1)..Point::new(2, 1),
13266            ]
13267        );
13268        editor
13269    });
13270
13271    // Refreshing selections is a no-op when excerpts haven't changed.
13272    _ = editor.update(cx, |editor, window, cx| {
13273        editor.change_selections(None, window, cx, |s| s.refresh());
13274        assert_eq!(
13275            editor.selections.ranges(cx),
13276            [
13277                Point::new(1, 3)..Point::new(1, 3),
13278                Point::new(2, 1)..Point::new(2, 1),
13279            ]
13280        );
13281    });
13282
13283    multibuffer.update(cx, |multibuffer, cx| {
13284        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
13285    });
13286    _ = editor.update(cx, |editor, window, cx| {
13287        // Removing an excerpt causes the first selection to become degenerate.
13288        assert_eq!(
13289            editor.selections.ranges(cx),
13290            [
13291                Point::new(0, 0)..Point::new(0, 0),
13292                Point::new(0, 1)..Point::new(0, 1)
13293            ]
13294        );
13295
13296        // Refreshing selections will relocate the first selection to the original buffer
13297        // location.
13298        editor.change_selections(None, window, cx, |s| s.refresh());
13299        assert_eq!(
13300            editor.selections.ranges(cx),
13301            [
13302                Point::new(0, 1)..Point::new(0, 1),
13303                Point::new(0, 3)..Point::new(0, 3)
13304            ]
13305        );
13306        assert!(editor.selections.pending_anchor().is_some());
13307    });
13308}
13309
13310#[gpui::test]
13311fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
13312    init_test(cx, |_| {});
13313
13314    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
13315    let mut excerpt1_id = None;
13316    let multibuffer = cx.new(|cx| {
13317        let mut multibuffer = MultiBuffer::new(ReadWrite);
13318        excerpt1_id = multibuffer
13319            .push_excerpts(
13320                buffer.clone(),
13321                [
13322                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
13323                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
13324                ],
13325                cx,
13326            )
13327            .into_iter()
13328            .next();
13329        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
13330        multibuffer
13331    });
13332
13333    let editor = cx.add_window(|window, cx| {
13334        let mut editor = build_editor(multibuffer.clone(), window, cx);
13335        let snapshot = editor.snapshot(window, cx);
13336        editor.begin_selection(
13337            Point::new(1, 3).to_display_point(&snapshot),
13338            false,
13339            1,
13340            window,
13341            cx,
13342        );
13343        assert_eq!(
13344            editor.selections.ranges(cx),
13345            [Point::new(1, 3)..Point::new(1, 3)]
13346        );
13347        editor
13348    });
13349
13350    multibuffer.update(cx, |multibuffer, cx| {
13351        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
13352    });
13353    _ = editor.update(cx, |editor, window, cx| {
13354        assert_eq!(
13355            editor.selections.ranges(cx),
13356            [Point::new(0, 0)..Point::new(0, 0)]
13357        );
13358
13359        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
13360        editor.change_selections(None, window, cx, |s| s.refresh());
13361        assert_eq!(
13362            editor.selections.ranges(cx),
13363            [Point::new(0, 3)..Point::new(0, 3)]
13364        );
13365        assert!(editor.selections.pending_anchor().is_some());
13366    });
13367}
13368
13369#[gpui::test]
13370async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
13371    init_test(cx, |_| {});
13372
13373    let language = Arc::new(
13374        Language::new(
13375            LanguageConfig {
13376                brackets: BracketPairConfig {
13377                    pairs: vec![
13378                        BracketPair {
13379                            start: "{".to_string(),
13380                            end: "}".to_string(),
13381                            close: true,
13382                            surround: true,
13383                            newline: true,
13384                        },
13385                        BracketPair {
13386                            start: "/* ".to_string(),
13387                            end: " */".to_string(),
13388                            close: true,
13389                            surround: true,
13390                            newline: true,
13391                        },
13392                    ],
13393                    ..Default::default()
13394                },
13395                ..Default::default()
13396            },
13397            Some(tree_sitter_rust::LANGUAGE.into()),
13398        )
13399        .with_indents_query("")
13400        .unwrap(),
13401    );
13402
13403    let text = concat!(
13404        "{   }\n",     //
13405        "  x\n",       //
13406        "  /*   */\n", //
13407        "x\n",         //
13408        "{{} }\n",     //
13409    );
13410
13411    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
13412    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13413    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13414    editor
13415        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
13416        .await;
13417
13418    editor.update_in(cx, |editor, window, cx| {
13419        editor.change_selections(None, window, cx, |s| {
13420            s.select_display_ranges([
13421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
13422                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
13423                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
13424            ])
13425        });
13426        editor.newline(&Newline, window, cx);
13427
13428        assert_eq!(
13429            editor.buffer().read(cx).read(cx).text(),
13430            concat!(
13431                "{ \n",    // Suppress rustfmt
13432                "\n",      //
13433                "}\n",     //
13434                "  x\n",   //
13435                "  /* \n", //
13436                "  \n",    //
13437                "  */\n",  //
13438                "x\n",     //
13439                "{{} \n",  //
13440                "}\n",     //
13441            )
13442        );
13443    });
13444}
13445
13446#[gpui::test]
13447fn test_highlighted_ranges(cx: &mut TestAppContext) {
13448    init_test(cx, |_| {});
13449
13450    let editor = cx.add_window(|window, cx| {
13451        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
13452        build_editor(buffer.clone(), window, cx)
13453    });
13454
13455    _ = editor.update(cx, |editor, window, cx| {
13456        struct Type1;
13457        struct Type2;
13458
13459        let buffer = editor.buffer.read(cx).snapshot(cx);
13460
13461        let anchor_range =
13462            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
13463
13464        editor.highlight_background::<Type1>(
13465            &[
13466                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
13467                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
13468                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
13469                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
13470            ],
13471            |_| Hsla::red(),
13472            cx,
13473        );
13474        editor.highlight_background::<Type2>(
13475            &[
13476                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
13477                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
13478                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
13479                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
13480            ],
13481            |_| Hsla::green(),
13482            cx,
13483        );
13484
13485        let snapshot = editor.snapshot(window, cx);
13486        let mut highlighted_ranges = editor.background_highlights_in_range(
13487            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
13488            &snapshot,
13489            cx.theme().colors(),
13490        );
13491        // Enforce a consistent ordering based on color without relying on the ordering of the
13492        // highlight's `TypeId` which is non-executor.
13493        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
13494        assert_eq!(
13495            highlighted_ranges,
13496            &[
13497                (
13498                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
13499                    Hsla::red(),
13500                ),
13501                (
13502                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
13503                    Hsla::red(),
13504                ),
13505                (
13506                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
13507                    Hsla::green(),
13508                ),
13509                (
13510                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
13511                    Hsla::green(),
13512                ),
13513            ]
13514        );
13515        assert_eq!(
13516            editor.background_highlights_in_range(
13517                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
13518                &snapshot,
13519                cx.theme().colors(),
13520            ),
13521            &[(
13522                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
13523                Hsla::red(),
13524            )]
13525        );
13526    });
13527}
13528
13529#[gpui::test]
13530async fn test_following(cx: &mut TestAppContext) {
13531    init_test(cx, |_| {});
13532
13533    let fs = FakeFs::new(cx.executor());
13534    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13535
13536    let buffer = project.update(cx, |project, cx| {
13537        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
13538        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
13539    });
13540    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
13541    let follower = cx.update(|cx| {
13542        cx.open_window(
13543            WindowOptions {
13544                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
13545                    gpui::Point::new(px(0.), px(0.)),
13546                    gpui::Point::new(px(10.), px(80.)),
13547                ))),
13548                ..Default::default()
13549            },
13550            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
13551        )
13552        .unwrap()
13553    });
13554
13555    let is_still_following = Rc::new(RefCell::new(true));
13556    let follower_edit_event_count = Rc::new(RefCell::new(0));
13557    let pending_update = Rc::new(RefCell::new(None));
13558    let leader_entity = leader.root(cx).unwrap();
13559    let follower_entity = follower.root(cx).unwrap();
13560    _ = follower.update(cx, {
13561        let update = pending_update.clone();
13562        let is_still_following = is_still_following.clone();
13563        let follower_edit_event_count = follower_edit_event_count.clone();
13564        |_, window, cx| {
13565            cx.subscribe_in(
13566                &leader_entity,
13567                window,
13568                move |_, leader, event, window, cx| {
13569                    leader.read(cx).add_event_to_update_proto(
13570                        event,
13571                        &mut update.borrow_mut(),
13572                        window,
13573                        cx,
13574                    );
13575                },
13576            )
13577            .detach();
13578
13579            cx.subscribe_in(
13580                &follower_entity,
13581                window,
13582                move |_, _, event: &EditorEvent, _window, _cx| {
13583                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
13584                        *is_still_following.borrow_mut() = false;
13585                    }
13586
13587                    if let EditorEvent::BufferEdited = event {
13588                        *follower_edit_event_count.borrow_mut() += 1;
13589                    }
13590                },
13591            )
13592            .detach();
13593        }
13594    });
13595
13596    // Update the selections only
13597    _ = leader.update(cx, |leader, window, cx| {
13598        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13599    });
13600    follower
13601        .update(cx, |follower, window, cx| {
13602            follower.apply_update_proto(
13603                &project,
13604                pending_update.borrow_mut().take().unwrap(),
13605                window,
13606                cx,
13607            )
13608        })
13609        .unwrap()
13610        .await
13611        .unwrap();
13612    _ = follower.update(cx, |follower, _, cx| {
13613        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
13614    });
13615    assert!(*is_still_following.borrow());
13616    assert_eq!(*follower_edit_event_count.borrow(), 0);
13617
13618    // Update the scroll position only
13619    _ = leader.update(cx, |leader, window, cx| {
13620        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13621    });
13622    follower
13623        .update(cx, |follower, window, cx| {
13624            follower.apply_update_proto(
13625                &project,
13626                pending_update.borrow_mut().take().unwrap(),
13627                window,
13628                cx,
13629            )
13630        })
13631        .unwrap()
13632        .await
13633        .unwrap();
13634    assert_eq!(
13635        follower
13636            .update(cx, |follower, _, cx| follower.scroll_position(cx))
13637            .unwrap(),
13638        gpui::Point::new(1.5, 3.5)
13639    );
13640    assert!(*is_still_following.borrow());
13641    assert_eq!(*follower_edit_event_count.borrow(), 0);
13642
13643    // Update the selections and scroll position. The follower's scroll position is updated
13644    // via autoscroll, not via the leader's exact scroll position.
13645    _ = leader.update(cx, |leader, window, cx| {
13646        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
13647        leader.request_autoscroll(Autoscroll::newest(), cx);
13648        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13649    });
13650    follower
13651        .update(cx, |follower, window, cx| {
13652            follower.apply_update_proto(
13653                &project,
13654                pending_update.borrow_mut().take().unwrap(),
13655                window,
13656                cx,
13657            )
13658        })
13659        .unwrap()
13660        .await
13661        .unwrap();
13662    _ = follower.update(cx, |follower, _, cx| {
13663        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
13664        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
13665    });
13666    assert!(*is_still_following.borrow());
13667
13668    // Creating a pending selection that precedes another selection
13669    _ = leader.update(cx, |leader, window, cx| {
13670        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13671        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
13672    });
13673    follower
13674        .update(cx, |follower, window, cx| {
13675            follower.apply_update_proto(
13676                &project,
13677                pending_update.borrow_mut().take().unwrap(),
13678                window,
13679                cx,
13680            )
13681        })
13682        .unwrap()
13683        .await
13684        .unwrap();
13685    _ = follower.update(cx, |follower, _, cx| {
13686        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
13687    });
13688    assert!(*is_still_following.borrow());
13689
13690    // Extend the pending selection so that it surrounds another selection
13691    _ = leader.update(cx, |leader, window, cx| {
13692        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
13693    });
13694    follower
13695        .update(cx, |follower, window, cx| {
13696            follower.apply_update_proto(
13697                &project,
13698                pending_update.borrow_mut().take().unwrap(),
13699                window,
13700                cx,
13701            )
13702        })
13703        .unwrap()
13704        .await
13705        .unwrap();
13706    _ = follower.update(cx, |follower, _, cx| {
13707        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
13708    });
13709
13710    // Scrolling locally breaks the follow
13711    _ = follower.update(cx, |follower, window, cx| {
13712        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13713        follower.set_scroll_anchor(
13714            ScrollAnchor {
13715                anchor: top_anchor,
13716                offset: gpui::Point::new(0.0, 0.5),
13717            },
13718            window,
13719            cx,
13720        );
13721    });
13722    assert!(!(*is_still_following.borrow()));
13723}
13724
13725#[gpui::test]
13726async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13727    init_test(cx, |_| {});
13728
13729    let fs = FakeFs::new(cx.executor());
13730    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13731    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13732    let pane = workspace
13733        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13734        .unwrap();
13735
13736    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13737
13738    let leader = pane.update_in(cx, |_, window, cx| {
13739        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13740        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13741    });
13742
13743    // Start following the editor when it has no excerpts.
13744    let mut state_message =
13745        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13746    let workspace_entity = workspace.root(cx).unwrap();
13747    let follower_1 = cx
13748        .update_window(*workspace.deref(), |_, window, cx| {
13749            Editor::from_state_proto(
13750                workspace_entity,
13751                ViewId {
13752                    creator: CollaboratorId::PeerId(PeerId::default()),
13753                    id: 0,
13754                },
13755                &mut state_message,
13756                window,
13757                cx,
13758            )
13759        })
13760        .unwrap()
13761        .unwrap()
13762        .await
13763        .unwrap();
13764
13765    let update_message = Rc::new(RefCell::new(None));
13766    follower_1.update_in(cx, {
13767        let update = update_message.clone();
13768        |_, window, cx| {
13769            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13770                leader.read(cx).add_event_to_update_proto(
13771                    event,
13772                    &mut update.borrow_mut(),
13773                    window,
13774                    cx,
13775                );
13776            })
13777            .detach();
13778        }
13779    });
13780
13781    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13782        (
13783            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13784            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13785        )
13786    });
13787
13788    // Insert some excerpts.
13789    leader.update(cx, |leader, cx| {
13790        leader.buffer.update(cx, |multibuffer, cx| {
13791            multibuffer.set_excerpts_for_path(
13792                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13793                buffer_1.clone(),
13794                vec![
13795                    Point::row_range(0..3),
13796                    Point::row_range(1..6),
13797                    Point::row_range(12..15),
13798                ],
13799                0,
13800                cx,
13801            );
13802            multibuffer.set_excerpts_for_path(
13803                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13804                buffer_2.clone(),
13805                vec![Point::row_range(0..6), Point::row_range(8..12)],
13806                0,
13807                cx,
13808            );
13809        });
13810    });
13811
13812    // Apply the update of adding the excerpts.
13813    follower_1
13814        .update_in(cx, |follower, window, cx| {
13815            follower.apply_update_proto(
13816                &project,
13817                update_message.borrow().clone().unwrap(),
13818                window,
13819                cx,
13820            )
13821        })
13822        .await
13823        .unwrap();
13824    assert_eq!(
13825        follower_1.update(cx, |editor, cx| editor.text(cx)),
13826        leader.update(cx, |editor, cx| editor.text(cx))
13827    );
13828    update_message.borrow_mut().take();
13829
13830    // Start following separately after it already has excerpts.
13831    let mut state_message =
13832        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13833    let workspace_entity = workspace.root(cx).unwrap();
13834    let follower_2 = cx
13835        .update_window(*workspace.deref(), |_, window, cx| {
13836            Editor::from_state_proto(
13837                workspace_entity,
13838                ViewId {
13839                    creator: CollaboratorId::PeerId(PeerId::default()),
13840                    id: 0,
13841                },
13842                &mut state_message,
13843                window,
13844                cx,
13845            )
13846        })
13847        .unwrap()
13848        .unwrap()
13849        .await
13850        .unwrap();
13851    assert_eq!(
13852        follower_2.update(cx, |editor, cx| editor.text(cx)),
13853        leader.update(cx, |editor, cx| editor.text(cx))
13854    );
13855
13856    // Remove some excerpts.
13857    leader.update(cx, |leader, cx| {
13858        leader.buffer.update(cx, |multibuffer, cx| {
13859            let excerpt_ids = multibuffer.excerpt_ids();
13860            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13861            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13862        });
13863    });
13864
13865    // Apply the update of removing the excerpts.
13866    follower_1
13867        .update_in(cx, |follower, window, cx| {
13868            follower.apply_update_proto(
13869                &project,
13870                update_message.borrow().clone().unwrap(),
13871                window,
13872                cx,
13873            )
13874        })
13875        .await
13876        .unwrap();
13877    follower_2
13878        .update_in(cx, |follower, window, cx| {
13879            follower.apply_update_proto(
13880                &project,
13881                update_message.borrow().clone().unwrap(),
13882                window,
13883                cx,
13884            )
13885        })
13886        .await
13887        .unwrap();
13888    update_message.borrow_mut().take();
13889    assert_eq!(
13890        follower_1.update(cx, |editor, cx| editor.text(cx)),
13891        leader.update(cx, |editor, cx| editor.text(cx))
13892    );
13893}
13894
13895#[gpui::test]
13896async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13897    init_test(cx, |_| {});
13898
13899    let mut cx = EditorTestContext::new(cx).await;
13900    let lsp_store =
13901        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13902
13903    cx.set_state(indoc! {"
13904        ˇfn func(abc def: i32) -> u32 {
13905        }
13906    "});
13907
13908    cx.update(|_, cx| {
13909        lsp_store.update(cx, |lsp_store, cx| {
13910            lsp_store
13911                .update_diagnostics(
13912                    LanguageServerId(0),
13913                    lsp::PublishDiagnosticsParams {
13914                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13915                        version: None,
13916                        diagnostics: vec![
13917                            lsp::Diagnostic {
13918                                range: lsp::Range::new(
13919                                    lsp::Position::new(0, 11),
13920                                    lsp::Position::new(0, 12),
13921                                ),
13922                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13923                                ..Default::default()
13924                            },
13925                            lsp::Diagnostic {
13926                                range: lsp::Range::new(
13927                                    lsp::Position::new(0, 12),
13928                                    lsp::Position::new(0, 15),
13929                                ),
13930                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13931                                ..Default::default()
13932                            },
13933                            lsp::Diagnostic {
13934                                range: lsp::Range::new(
13935                                    lsp::Position::new(0, 25),
13936                                    lsp::Position::new(0, 28),
13937                                ),
13938                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13939                                ..Default::default()
13940                            },
13941                        ],
13942                    },
13943                    None,
13944                    DiagnosticSourceKind::Pushed,
13945                    &[],
13946                    cx,
13947                )
13948                .unwrap()
13949        });
13950    });
13951
13952    executor.run_until_parked();
13953
13954    cx.update_editor(|editor, window, cx| {
13955        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13956    });
13957
13958    cx.assert_editor_state(indoc! {"
13959        fn func(abc def: i32) -> ˇu32 {
13960        }
13961    "});
13962
13963    cx.update_editor(|editor, window, cx| {
13964        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13965    });
13966
13967    cx.assert_editor_state(indoc! {"
13968        fn func(abc ˇdef: i32) -> u32 {
13969        }
13970    "});
13971
13972    cx.update_editor(|editor, window, cx| {
13973        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13974    });
13975
13976    cx.assert_editor_state(indoc! {"
13977        fn func(abcˇ def: i32) -> u32 {
13978        }
13979    "});
13980
13981    cx.update_editor(|editor, window, cx| {
13982        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13983    });
13984
13985    cx.assert_editor_state(indoc! {"
13986        fn func(abc def: i32) -> ˇu32 {
13987        }
13988    "});
13989}
13990
13991#[gpui::test]
13992async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13993    init_test(cx, |_| {});
13994
13995    let mut cx = EditorTestContext::new(cx).await;
13996
13997    let diff_base = r#"
13998        use some::mod;
13999
14000        const A: u32 = 42;
14001
14002        fn main() {
14003            println!("hello");
14004
14005            println!("world");
14006        }
14007        "#
14008    .unindent();
14009
14010    // Edits are modified, removed, modified, added
14011    cx.set_state(
14012        &r#"
14013        use some::modified;
14014
14015        ˇ
14016        fn main() {
14017            println!("hello there");
14018
14019            println!("around the");
14020            println!("world");
14021        }
14022        "#
14023        .unindent(),
14024    );
14025
14026    cx.set_head_text(&diff_base);
14027    executor.run_until_parked();
14028
14029    cx.update_editor(|editor, window, cx| {
14030        //Wrap around the bottom of the buffer
14031        for _ in 0..3 {
14032            editor.go_to_next_hunk(&GoToHunk, window, cx);
14033        }
14034    });
14035
14036    cx.assert_editor_state(
14037        &r#"
14038        ˇuse some::modified;
14039
14040
14041        fn main() {
14042            println!("hello there");
14043
14044            println!("around the");
14045            println!("world");
14046        }
14047        "#
14048        .unindent(),
14049    );
14050
14051    cx.update_editor(|editor, window, cx| {
14052        //Wrap around the top of the buffer
14053        for _ in 0..2 {
14054            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
14055        }
14056    });
14057
14058    cx.assert_editor_state(
14059        &r#"
14060        use some::modified;
14061
14062
14063        fn main() {
14064        ˇ    println!("hello there");
14065
14066            println!("around the");
14067            println!("world");
14068        }
14069        "#
14070        .unindent(),
14071    );
14072
14073    cx.update_editor(|editor, window, cx| {
14074        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
14075    });
14076
14077    cx.assert_editor_state(
14078        &r#"
14079        use some::modified;
14080
14081        ˇ
14082        fn main() {
14083            println!("hello there");
14084
14085            println!("around the");
14086            println!("world");
14087        }
14088        "#
14089        .unindent(),
14090    );
14091
14092    cx.update_editor(|editor, window, cx| {
14093        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
14094    });
14095
14096    cx.assert_editor_state(
14097        &r#"
14098        ˇuse some::modified;
14099
14100
14101        fn main() {
14102            println!("hello there");
14103
14104            println!("around the");
14105            println!("world");
14106        }
14107        "#
14108        .unindent(),
14109    );
14110
14111    cx.update_editor(|editor, window, cx| {
14112        for _ in 0..2 {
14113            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
14114        }
14115    });
14116
14117    cx.assert_editor_state(
14118        &r#"
14119        use some::modified;
14120
14121
14122        fn main() {
14123        ˇ    println!("hello there");
14124
14125            println!("around the");
14126            println!("world");
14127        }
14128        "#
14129        .unindent(),
14130    );
14131
14132    cx.update_editor(|editor, window, cx| {
14133        editor.fold(&Fold, window, cx);
14134    });
14135
14136    cx.update_editor(|editor, window, cx| {
14137        editor.go_to_next_hunk(&GoToHunk, window, cx);
14138    });
14139
14140    cx.assert_editor_state(
14141        &r#"
14142        ˇuse some::modified;
14143
14144
14145        fn main() {
14146            println!("hello there");
14147
14148            println!("around the");
14149            println!("world");
14150        }
14151        "#
14152        .unindent(),
14153    );
14154}
14155
14156#[test]
14157fn test_split_words() {
14158    fn split(text: &str) -> Vec<&str> {
14159        split_words(text).collect()
14160    }
14161
14162    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
14163    assert_eq!(split("hello_world"), &["hello_", "world"]);
14164    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
14165    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
14166    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
14167    assert_eq!(split("helloworld"), &["helloworld"]);
14168
14169    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
14170}
14171
14172#[gpui::test]
14173async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
14174    init_test(cx, |_| {});
14175
14176    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
14177    let mut assert = |before, after| {
14178        let _state_context = cx.set_state(before);
14179        cx.run_until_parked();
14180        cx.update_editor(|editor, window, cx| {
14181            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
14182        });
14183        cx.run_until_parked();
14184        cx.assert_editor_state(after);
14185    };
14186
14187    // Outside bracket jumps to outside of matching bracket
14188    assert("console.logˇ(var);", "console.log(var)ˇ;");
14189    assert("console.log(var)ˇ;", "console.logˇ(var);");
14190
14191    // Inside bracket jumps to inside of matching bracket
14192    assert("console.log(ˇvar);", "console.log(varˇ);");
14193    assert("console.log(varˇ);", "console.log(ˇvar);");
14194
14195    // When outside a bracket and inside, favor jumping to the inside bracket
14196    assert(
14197        "console.log('foo', [1, 2, 3]ˇ);",
14198        "console.log(ˇ'foo', [1, 2, 3]);",
14199    );
14200    assert(
14201        "console.log(ˇ'foo', [1, 2, 3]);",
14202        "console.log('foo', [1, 2, 3]ˇ);",
14203    );
14204
14205    // Bias forward if two options are equally likely
14206    assert(
14207        "let result = curried_fun()ˇ();",
14208        "let result = curried_fun()()ˇ;",
14209    );
14210
14211    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
14212    assert(
14213        indoc! {"
14214            function test() {
14215                console.log('test')ˇ
14216            }"},
14217        indoc! {"
14218            function test() {
14219                console.logˇ('test')
14220            }"},
14221    );
14222}
14223
14224#[gpui::test]
14225async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
14226    init_test(cx, |_| {});
14227
14228    let fs = FakeFs::new(cx.executor());
14229    fs.insert_tree(
14230        path!("/a"),
14231        json!({
14232            "main.rs": "fn main() { let a = 5; }",
14233            "other.rs": "// Test file",
14234        }),
14235    )
14236    .await;
14237    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
14238
14239    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14240    language_registry.add(Arc::new(Language::new(
14241        LanguageConfig {
14242            name: "Rust".into(),
14243            matcher: LanguageMatcher {
14244                path_suffixes: vec!["rs".to_string()],
14245                ..Default::default()
14246            },
14247            brackets: BracketPairConfig {
14248                pairs: vec![BracketPair {
14249                    start: "{".to_string(),
14250                    end: "}".to_string(),
14251                    close: true,
14252                    surround: true,
14253                    newline: true,
14254                }],
14255                disabled_scopes_by_bracket_ix: Vec::new(),
14256            },
14257            ..Default::default()
14258        },
14259        Some(tree_sitter_rust::LANGUAGE.into()),
14260    )));
14261    let mut fake_servers = language_registry.register_fake_lsp(
14262        "Rust",
14263        FakeLspAdapter {
14264            capabilities: lsp::ServerCapabilities {
14265                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
14266                    first_trigger_character: "{".to_string(),
14267                    more_trigger_character: None,
14268                }),
14269                ..Default::default()
14270            },
14271            ..Default::default()
14272        },
14273    );
14274
14275    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14276
14277    let cx = &mut VisualTestContext::from_window(*workspace, cx);
14278
14279    let worktree_id = workspace
14280        .update(cx, |workspace, _, cx| {
14281            workspace.project().update(cx, |project, cx| {
14282                project.worktrees(cx).next().unwrap().read(cx).id()
14283            })
14284        })
14285        .unwrap();
14286
14287    let buffer = project
14288        .update(cx, |project, cx| {
14289            project.open_local_buffer(path!("/a/main.rs"), cx)
14290        })
14291        .await
14292        .unwrap();
14293    let editor_handle = workspace
14294        .update(cx, |workspace, window, cx| {
14295            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
14296        })
14297        .unwrap()
14298        .await
14299        .unwrap()
14300        .downcast::<Editor>()
14301        .unwrap();
14302
14303    cx.executor().start_waiting();
14304    let fake_server = fake_servers.next().await.unwrap();
14305
14306    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
14307        |params, _| async move {
14308            assert_eq!(
14309                params.text_document_position.text_document.uri,
14310                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
14311            );
14312            assert_eq!(
14313                params.text_document_position.position,
14314                lsp::Position::new(0, 21),
14315            );
14316
14317            Ok(Some(vec![lsp::TextEdit {
14318                new_text: "]".to_string(),
14319                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14320            }]))
14321        },
14322    );
14323
14324    editor_handle.update_in(cx, |editor, window, cx| {
14325        window.focus(&editor.focus_handle(cx));
14326        editor.change_selections(None, window, cx, |s| {
14327            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
14328        });
14329        editor.handle_input("{", window, cx);
14330    });
14331
14332    cx.executor().run_until_parked();
14333
14334    buffer.update(cx, |buffer, _| {
14335        assert_eq!(
14336            buffer.text(),
14337            "fn main() { let a = {5}; }",
14338            "No extra braces from on type formatting should appear in the buffer"
14339        )
14340    });
14341}
14342
14343#[gpui::test]
14344async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
14345    init_test(cx, |_| {});
14346
14347    let fs = FakeFs::new(cx.executor());
14348    fs.insert_tree(
14349        path!("/a"),
14350        json!({
14351            "main.rs": "fn main() { let a = 5; }",
14352            "other.rs": "// Test file",
14353        }),
14354    )
14355    .await;
14356
14357    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
14358
14359    let server_restarts = Arc::new(AtomicUsize::new(0));
14360    let closure_restarts = Arc::clone(&server_restarts);
14361    let language_server_name = "test language server";
14362    let language_name: LanguageName = "Rust".into();
14363
14364    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14365    language_registry.add(Arc::new(Language::new(
14366        LanguageConfig {
14367            name: language_name.clone(),
14368            matcher: LanguageMatcher {
14369                path_suffixes: vec!["rs".to_string()],
14370                ..Default::default()
14371            },
14372            ..Default::default()
14373        },
14374        Some(tree_sitter_rust::LANGUAGE.into()),
14375    )));
14376    let mut fake_servers = language_registry.register_fake_lsp(
14377        "Rust",
14378        FakeLspAdapter {
14379            name: language_server_name,
14380            initialization_options: Some(json!({
14381                "testOptionValue": true
14382            })),
14383            initializer: Some(Box::new(move |fake_server| {
14384                let task_restarts = Arc::clone(&closure_restarts);
14385                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
14386                    task_restarts.fetch_add(1, atomic::Ordering::Release);
14387                    futures::future::ready(Ok(()))
14388                });
14389            })),
14390            ..Default::default()
14391        },
14392    );
14393
14394    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14395    let _buffer = project
14396        .update(cx, |project, cx| {
14397            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
14398        })
14399        .await
14400        .unwrap();
14401    let _fake_server = fake_servers.next().await.unwrap();
14402    update_test_language_settings(cx, |language_settings| {
14403        language_settings.languages.insert(
14404            language_name.clone(),
14405            LanguageSettingsContent {
14406                tab_size: NonZeroU32::new(8),
14407                ..Default::default()
14408            },
14409        );
14410    });
14411    cx.executor().run_until_parked();
14412    assert_eq!(
14413        server_restarts.load(atomic::Ordering::Acquire),
14414        0,
14415        "Should not restart LSP server on an unrelated change"
14416    );
14417
14418    update_test_project_settings(cx, |project_settings| {
14419        project_settings.lsp.insert(
14420            "Some other server name".into(),
14421            LspSettings {
14422                binary: None,
14423                settings: None,
14424                initialization_options: Some(json!({
14425                    "some other init value": false
14426                })),
14427                enable_lsp_tasks: false,
14428            },
14429        );
14430    });
14431    cx.executor().run_until_parked();
14432    assert_eq!(
14433        server_restarts.load(atomic::Ordering::Acquire),
14434        0,
14435        "Should not restart LSP server on an unrelated LSP settings change"
14436    );
14437
14438    update_test_project_settings(cx, |project_settings| {
14439        project_settings.lsp.insert(
14440            language_server_name.into(),
14441            LspSettings {
14442                binary: None,
14443                settings: None,
14444                initialization_options: Some(json!({
14445                    "anotherInitValue": false
14446                })),
14447                enable_lsp_tasks: false,
14448            },
14449        );
14450    });
14451    cx.executor().run_until_parked();
14452    assert_eq!(
14453        server_restarts.load(atomic::Ordering::Acquire),
14454        1,
14455        "Should restart LSP server on a related LSP settings change"
14456    );
14457
14458    update_test_project_settings(cx, |project_settings| {
14459        project_settings.lsp.insert(
14460            language_server_name.into(),
14461            LspSettings {
14462                binary: None,
14463                settings: None,
14464                initialization_options: Some(json!({
14465                    "anotherInitValue": false
14466                })),
14467                enable_lsp_tasks: false,
14468            },
14469        );
14470    });
14471    cx.executor().run_until_parked();
14472    assert_eq!(
14473        server_restarts.load(atomic::Ordering::Acquire),
14474        1,
14475        "Should not restart LSP server on a related LSP settings change that is the same"
14476    );
14477
14478    update_test_project_settings(cx, |project_settings| {
14479        project_settings.lsp.insert(
14480            language_server_name.into(),
14481            LspSettings {
14482                binary: None,
14483                settings: None,
14484                initialization_options: None,
14485                enable_lsp_tasks: false,
14486            },
14487        );
14488    });
14489    cx.executor().run_until_parked();
14490    assert_eq!(
14491        server_restarts.load(atomic::Ordering::Acquire),
14492        2,
14493        "Should restart LSP server on another related LSP settings change"
14494    );
14495}
14496
14497#[gpui::test]
14498async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
14499    init_test(cx, |_| {});
14500
14501    let mut cx = EditorLspTestContext::new_rust(
14502        lsp::ServerCapabilities {
14503            completion_provider: Some(lsp::CompletionOptions {
14504                trigger_characters: Some(vec![".".to_string()]),
14505                resolve_provider: Some(true),
14506                ..Default::default()
14507            }),
14508            ..Default::default()
14509        },
14510        cx,
14511    )
14512    .await;
14513
14514    cx.set_state("fn main() { let a = 2ˇ; }");
14515    cx.simulate_keystroke(".");
14516    let completion_item = lsp::CompletionItem {
14517        label: "some".into(),
14518        kind: Some(lsp::CompletionItemKind::SNIPPET),
14519        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
14520        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
14521            kind: lsp::MarkupKind::Markdown,
14522            value: "```rust\nSome(2)\n```".to_string(),
14523        })),
14524        deprecated: Some(false),
14525        sort_text: Some("fffffff2".to_string()),
14526        filter_text: Some("some".to_string()),
14527        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
14528        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14529            range: lsp::Range {
14530                start: lsp::Position {
14531                    line: 0,
14532                    character: 22,
14533                },
14534                end: lsp::Position {
14535                    line: 0,
14536                    character: 22,
14537                },
14538            },
14539            new_text: "Some(2)".to_string(),
14540        })),
14541        additional_text_edits: Some(vec![lsp::TextEdit {
14542            range: lsp::Range {
14543                start: lsp::Position {
14544                    line: 0,
14545                    character: 20,
14546                },
14547                end: lsp::Position {
14548                    line: 0,
14549                    character: 22,
14550                },
14551            },
14552            new_text: "".to_string(),
14553        }]),
14554        ..Default::default()
14555    };
14556
14557    let closure_completion_item = completion_item.clone();
14558    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14559        let task_completion_item = closure_completion_item.clone();
14560        async move {
14561            Ok(Some(lsp::CompletionResponse::Array(vec![
14562                task_completion_item,
14563            ])))
14564        }
14565    });
14566
14567    request.next().await;
14568
14569    cx.condition(|editor, _| editor.context_menu_visible())
14570        .await;
14571    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
14572        editor
14573            .confirm_completion(&ConfirmCompletion::default(), window, cx)
14574            .unwrap()
14575    });
14576    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
14577
14578    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14579        let task_completion_item = completion_item.clone();
14580        async move { Ok(task_completion_item) }
14581    })
14582    .next()
14583    .await
14584    .unwrap();
14585    apply_additional_edits.await.unwrap();
14586    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
14587}
14588
14589#[gpui::test]
14590async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
14591    init_test(cx, |_| {});
14592
14593    let mut cx = EditorLspTestContext::new_rust(
14594        lsp::ServerCapabilities {
14595            completion_provider: Some(lsp::CompletionOptions {
14596                trigger_characters: Some(vec![".".to_string()]),
14597                resolve_provider: Some(true),
14598                ..Default::default()
14599            }),
14600            ..Default::default()
14601        },
14602        cx,
14603    )
14604    .await;
14605
14606    cx.set_state("fn main() { let a = 2ˇ; }");
14607    cx.simulate_keystroke(".");
14608
14609    let item1 = lsp::CompletionItem {
14610        label: "method id()".to_string(),
14611        filter_text: Some("id".to_string()),
14612        detail: None,
14613        documentation: None,
14614        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14615            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14616            new_text: ".id".to_string(),
14617        })),
14618        ..lsp::CompletionItem::default()
14619    };
14620
14621    let item2 = lsp::CompletionItem {
14622        label: "other".to_string(),
14623        filter_text: Some("other".to_string()),
14624        detail: None,
14625        documentation: None,
14626        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14627            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14628            new_text: ".other".to_string(),
14629        })),
14630        ..lsp::CompletionItem::default()
14631    };
14632
14633    let item1 = item1.clone();
14634    cx.set_request_handler::<lsp::request::Completion, _, _>({
14635        let item1 = item1.clone();
14636        move |_, _, _| {
14637            let item1 = item1.clone();
14638            let item2 = item2.clone();
14639            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
14640        }
14641    })
14642    .next()
14643    .await;
14644
14645    cx.condition(|editor, _| editor.context_menu_visible())
14646        .await;
14647    cx.update_editor(|editor, _, _| {
14648        let context_menu = editor.context_menu.borrow_mut();
14649        let context_menu = context_menu
14650            .as_ref()
14651            .expect("Should have the context menu deployed");
14652        match context_menu {
14653            CodeContextMenu::Completions(completions_menu) => {
14654                let completions = completions_menu.completions.borrow_mut();
14655                assert_eq!(
14656                    completions
14657                        .iter()
14658                        .map(|completion| &completion.label.text)
14659                        .collect::<Vec<_>>(),
14660                    vec!["method id()", "other"]
14661                )
14662            }
14663            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14664        }
14665    });
14666
14667    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
14668        let item1 = item1.clone();
14669        move |_, item_to_resolve, _| {
14670            let item1 = item1.clone();
14671            async move {
14672                if item1 == item_to_resolve {
14673                    Ok(lsp::CompletionItem {
14674                        label: "method id()".to_string(),
14675                        filter_text: Some("id".to_string()),
14676                        detail: Some("Now resolved!".to_string()),
14677                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
14678                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14679                            range: lsp::Range::new(
14680                                lsp::Position::new(0, 22),
14681                                lsp::Position::new(0, 22),
14682                            ),
14683                            new_text: ".id".to_string(),
14684                        })),
14685                        ..lsp::CompletionItem::default()
14686                    })
14687                } else {
14688                    Ok(item_to_resolve)
14689                }
14690            }
14691        }
14692    })
14693    .next()
14694    .await
14695    .unwrap();
14696    cx.run_until_parked();
14697
14698    cx.update_editor(|editor, window, cx| {
14699        editor.context_menu_next(&Default::default(), window, cx);
14700    });
14701
14702    cx.update_editor(|editor, _, _| {
14703        let context_menu = editor.context_menu.borrow_mut();
14704        let context_menu = context_menu
14705            .as_ref()
14706            .expect("Should have the context menu deployed");
14707        match context_menu {
14708            CodeContextMenu::Completions(completions_menu) => {
14709                let completions = completions_menu.completions.borrow_mut();
14710                assert_eq!(
14711                    completions
14712                        .iter()
14713                        .map(|completion| &completion.label.text)
14714                        .collect::<Vec<_>>(),
14715                    vec!["method id() Now resolved!", "other"],
14716                    "Should update first completion label, but not second as the filter text did not match."
14717                );
14718            }
14719            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14720        }
14721    });
14722}
14723
14724#[gpui::test]
14725async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14726    init_test(cx, |_| {});
14727    let mut cx = EditorLspTestContext::new_rust(
14728        lsp::ServerCapabilities {
14729            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14730            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14731            completion_provider: Some(lsp::CompletionOptions {
14732                resolve_provider: Some(true),
14733                ..Default::default()
14734            }),
14735            ..Default::default()
14736        },
14737        cx,
14738    )
14739    .await;
14740    cx.set_state(indoc! {"
14741        struct TestStruct {
14742            field: i32
14743        }
14744
14745        fn mainˇ() {
14746            let unused_var = 42;
14747            let test_struct = TestStruct { field: 42 };
14748        }
14749    "});
14750    let symbol_range = cx.lsp_range(indoc! {"
14751        struct TestStruct {
14752            field: i32
14753        }
14754
14755        «fn main»() {
14756            let unused_var = 42;
14757            let test_struct = TestStruct { field: 42 };
14758        }
14759    "});
14760    let mut hover_requests =
14761        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14762            Ok(Some(lsp::Hover {
14763                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14764                    kind: lsp::MarkupKind::Markdown,
14765                    value: "Function documentation".to_string(),
14766                }),
14767                range: Some(symbol_range),
14768            }))
14769        });
14770
14771    // Case 1: Test that code action menu hide hover popover
14772    cx.dispatch_action(Hover);
14773    hover_requests.next().await;
14774    cx.condition(|editor, _| editor.hover_state.visible()).await;
14775    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14776        move |_, _, _| async move {
14777            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14778                lsp::CodeAction {
14779                    title: "Remove unused variable".to_string(),
14780                    kind: Some(CodeActionKind::QUICKFIX),
14781                    edit: Some(lsp::WorkspaceEdit {
14782                        changes: Some(
14783                            [(
14784                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14785                                vec![lsp::TextEdit {
14786                                    range: lsp::Range::new(
14787                                        lsp::Position::new(5, 4),
14788                                        lsp::Position::new(5, 27),
14789                                    ),
14790                                    new_text: "".to_string(),
14791                                }],
14792                            )]
14793                            .into_iter()
14794                            .collect(),
14795                        ),
14796                        ..Default::default()
14797                    }),
14798                    ..Default::default()
14799                },
14800            )]))
14801        },
14802    );
14803    cx.update_editor(|editor, window, cx| {
14804        editor.toggle_code_actions(
14805            &ToggleCodeActions {
14806                deployed_from: None,
14807                quick_launch: false,
14808            },
14809            window,
14810            cx,
14811        );
14812    });
14813    code_action_requests.next().await;
14814    cx.run_until_parked();
14815    cx.condition(|editor, _| editor.context_menu_visible())
14816        .await;
14817    cx.update_editor(|editor, _, _| {
14818        assert!(
14819            !editor.hover_state.visible(),
14820            "Hover popover should be hidden when code action menu is shown"
14821        );
14822        // Hide code actions
14823        editor.context_menu.take();
14824    });
14825
14826    // Case 2: Test that code completions hide hover popover
14827    cx.dispatch_action(Hover);
14828    hover_requests.next().await;
14829    cx.condition(|editor, _| editor.hover_state.visible()).await;
14830    let counter = Arc::new(AtomicUsize::new(0));
14831    let mut completion_requests =
14832        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14833            let counter = counter.clone();
14834            async move {
14835                counter.fetch_add(1, atomic::Ordering::Release);
14836                Ok(Some(lsp::CompletionResponse::Array(vec![
14837                    lsp::CompletionItem {
14838                        label: "main".into(),
14839                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14840                        detail: Some("() -> ()".to_string()),
14841                        ..Default::default()
14842                    },
14843                    lsp::CompletionItem {
14844                        label: "TestStruct".into(),
14845                        kind: Some(lsp::CompletionItemKind::STRUCT),
14846                        detail: Some("struct TestStruct".to_string()),
14847                        ..Default::default()
14848                    },
14849                ])))
14850            }
14851        });
14852    cx.update_editor(|editor, window, cx| {
14853        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14854    });
14855    completion_requests.next().await;
14856    cx.condition(|editor, _| editor.context_menu_visible())
14857        .await;
14858    cx.update_editor(|editor, _, _| {
14859        assert!(
14860            !editor.hover_state.visible(),
14861            "Hover popover should be hidden when completion menu is shown"
14862        );
14863    });
14864}
14865
14866#[gpui::test]
14867async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14868    init_test(cx, |_| {});
14869
14870    let mut cx = EditorLspTestContext::new_rust(
14871        lsp::ServerCapabilities {
14872            completion_provider: Some(lsp::CompletionOptions {
14873                trigger_characters: Some(vec![".".to_string()]),
14874                resolve_provider: Some(true),
14875                ..Default::default()
14876            }),
14877            ..Default::default()
14878        },
14879        cx,
14880    )
14881    .await;
14882
14883    cx.set_state("fn main() { let a = 2ˇ; }");
14884    cx.simulate_keystroke(".");
14885
14886    let unresolved_item_1 = lsp::CompletionItem {
14887        label: "id".to_string(),
14888        filter_text: Some("id".to_string()),
14889        detail: None,
14890        documentation: None,
14891        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14892            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14893            new_text: ".id".to_string(),
14894        })),
14895        ..lsp::CompletionItem::default()
14896    };
14897    let resolved_item_1 = lsp::CompletionItem {
14898        additional_text_edits: Some(vec![lsp::TextEdit {
14899            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14900            new_text: "!!".to_string(),
14901        }]),
14902        ..unresolved_item_1.clone()
14903    };
14904    let unresolved_item_2 = lsp::CompletionItem {
14905        label: "other".to_string(),
14906        filter_text: Some("other".to_string()),
14907        detail: None,
14908        documentation: None,
14909        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14910            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14911            new_text: ".other".to_string(),
14912        })),
14913        ..lsp::CompletionItem::default()
14914    };
14915    let resolved_item_2 = lsp::CompletionItem {
14916        additional_text_edits: Some(vec![lsp::TextEdit {
14917            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14918            new_text: "??".to_string(),
14919        }]),
14920        ..unresolved_item_2.clone()
14921    };
14922
14923    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14924    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14925    cx.lsp
14926        .server
14927        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14928            let unresolved_item_1 = unresolved_item_1.clone();
14929            let resolved_item_1 = resolved_item_1.clone();
14930            let unresolved_item_2 = unresolved_item_2.clone();
14931            let resolved_item_2 = resolved_item_2.clone();
14932            let resolve_requests_1 = resolve_requests_1.clone();
14933            let resolve_requests_2 = resolve_requests_2.clone();
14934            move |unresolved_request, _| {
14935                let unresolved_item_1 = unresolved_item_1.clone();
14936                let resolved_item_1 = resolved_item_1.clone();
14937                let unresolved_item_2 = unresolved_item_2.clone();
14938                let resolved_item_2 = resolved_item_2.clone();
14939                let resolve_requests_1 = resolve_requests_1.clone();
14940                let resolve_requests_2 = resolve_requests_2.clone();
14941                async move {
14942                    if unresolved_request == unresolved_item_1 {
14943                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14944                        Ok(resolved_item_1.clone())
14945                    } else if unresolved_request == unresolved_item_2 {
14946                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14947                        Ok(resolved_item_2.clone())
14948                    } else {
14949                        panic!("Unexpected completion item {unresolved_request:?}")
14950                    }
14951                }
14952            }
14953        })
14954        .detach();
14955
14956    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14957        let unresolved_item_1 = unresolved_item_1.clone();
14958        let unresolved_item_2 = unresolved_item_2.clone();
14959        async move {
14960            Ok(Some(lsp::CompletionResponse::Array(vec![
14961                unresolved_item_1,
14962                unresolved_item_2,
14963            ])))
14964        }
14965    })
14966    .next()
14967    .await;
14968
14969    cx.condition(|editor, _| editor.context_menu_visible())
14970        .await;
14971    cx.update_editor(|editor, _, _| {
14972        let context_menu = editor.context_menu.borrow_mut();
14973        let context_menu = context_menu
14974            .as_ref()
14975            .expect("Should have the context menu deployed");
14976        match context_menu {
14977            CodeContextMenu::Completions(completions_menu) => {
14978                let completions = completions_menu.completions.borrow_mut();
14979                assert_eq!(
14980                    completions
14981                        .iter()
14982                        .map(|completion| &completion.label.text)
14983                        .collect::<Vec<_>>(),
14984                    vec!["id", "other"]
14985                )
14986            }
14987            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14988        }
14989    });
14990    cx.run_until_parked();
14991
14992    cx.update_editor(|editor, window, cx| {
14993        editor.context_menu_next(&ContextMenuNext, window, cx);
14994    });
14995    cx.run_until_parked();
14996    cx.update_editor(|editor, window, cx| {
14997        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14998    });
14999    cx.run_until_parked();
15000    cx.update_editor(|editor, window, cx| {
15001        editor.context_menu_next(&ContextMenuNext, window, cx);
15002    });
15003    cx.run_until_parked();
15004    cx.update_editor(|editor, window, cx| {
15005        editor
15006            .compose_completion(&ComposeCompletion::default(), window, cx)
15007            .expect("No task returned")
15008    })
15009    .await
15010    .expect("Completion failed");
15011    cx.run_until_parked();
15012
15013    cx.update_editor(|editor, _, cx| {
15014        assert_eq!(
15015            resolve_requests_1.load(atomic::Ordering::Acquire),
15016            1,
15017            "Should always resolve once despite multiple selections"
15018        );
15019        assert_eq!(
15020            resolve_requests_2.load(atomic::Ordering::Acquire),
15021            1,
15022            "Should always resolve once after multiple selections and applying the completion"
15023        );
15024        assert_eq!(
15025            editor.text(cx),
15026            "fn main() { let a = ??.other; }",
15027            "Should use resolved data when applying the completion"
15028        );
15029    });
15030}
15031
15032#[gpui::test]
15033async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
15034    init_test(cx, |_| {});
15035
15036    let item_0 = lsp::CompletionItem {
15037        label: "abs".into(),
15038        insert_text: Some("abs".into()),
15039        data: Some(json!({ "very": "special"})),
15040        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
15041        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15042            lsp::InsertReplaceEdit {
15043                new_text: "abs".to_string(),
15044                insert: lsp::Range::default(),
15045                replace: lsp::Range::default(),
15046            },
15047        )),
15048        ..lsp::CompletionItem::default()
15049    };
15050    let items = iter::once(item_0.clone())
15051        .chain((11..51).map(|i| lsp::CompletionItem {
15052            label: format!("item_{}", i),
15053            insert_text: Some(format!("item_{}", i)),
15054            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
15055            ..lsp::CompletionItem::default()
15056        }))
15057        .collect::<Vec<_>>();
15058
15059    let default_commit_characters = vec!["?".to_string()];
15060    let default_data = json!({ "default": "data"});
15061    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
15062    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
15063    let default_edit_range = lsp::Range {
15064        start: lsp::Position {
15065            line: 0,
15066            character: 5,
15067        },
15068        end: lsp::Position {
15069            line: 0,
15070            character: 5,
15071        },
15072    };
15073
15074    let mut cx = EditorLspTestContext::new_rust(
15075        lsp::ServerCapabilities {
15076            completion_provider: Some(lsp::CompletionOptions {
15077                trigger_characters: Some(vec![".".to_string()]),
15078                resolve_provider: Some(true),
15079                ..Default::default()
15080            }),
15081            ..Default::default()
15082        },
15083        cx,
15084    )
15085    .await;
15086
15087    cx.set_state("fn main() { let a = 2ˇ; }");
15088    cx.simulate_keystroke(".");
15089
15090    let completion_data = default_data.clone();
15091    let completion_characters = default_commit_characters.clone();
15092    let completion_items = items.clone();
15093    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
15094        let default_data = completion_data.clone();
15095        let default_commit_characters = completion_characters.clone();
15096        let items = completion_items.clone();
15097        async move {
15098            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
15099                items,
15100                item_defaults: Some(lsp::CompletionListItemDefaults {
15101                    data: Some(default_data.clone()),
15102                    commit_characters: Some(default_commit_characters.clone()),
15103                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
15104                        default_edit_range,
15105                    )),
15106                    insert_text_format: Some(default_insert_text_format),
15107                    insert_text_mode: Some(default_insert_text_mode),
15108                }),
15109                ..lsp::CompletionList::default()
15110            })))
15111        }
15112    })
15113    .next()
15114    .await;
15115
15116    let resolved_items = Arc::new(Mutex::new(Vec::new()));
15117    cx.lsp
15118        .server
15119        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
15120            let closure_resolved_items = resolved_items.clone();
15121            move |item_to_resolve, _| {
15122                let closure_resolved_items = closure_resolved_items.clone();
15123                async move {
15124                    closure_resolved_items.lock().push(item_to_resolve.clone());
15125                    Ok(item_to_resolve)
15126                }
15127            }
15128        })
15129        .detach();
15130
15131    cx.condition(|editor, _| editor.context_menu_visible())
15132        .await;
15133    cx.run_until_parked();
15134    cx.update_editor(|editor, _, _| {
15135        let menu = editor.context_menu.borrow_mut();
15136        match menu.as_ref().expect("should have the completions menu") {
15137            CodeContextMenu::Completions(completions_menu) => {
15138                assert_eq!(
15139                    completions_menu
15140                        .entries
15141                        .borrow()
15142                        .iter()
15143                        .map(|mat| mat.string.clone())
15144                        .collect::<Vec<String>>(),
15145                    items
15146                        .iter()
15147                        .map(|completion| completion.label.clone())
15148                        .collect::<Vec<String>>()
15149                );
15150            }
15151            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
15152        }
15153    });
15154    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
15155    // with 4 from the end.
15156    assert_eq!(
15157        *resolved_items.lock(),
15158        [&items[0..16], &items[items.len() - 4..items.len()]]
15159            .concat()
15160            .iter()
15161            .cloned()
15162            .map(|mut item| {
15163                if item.data.is_none() {
15164                    item.data = Some(default_data.clone());
15165                }
15166                item
15167            })
15168            .collect::<Vec<lsp::CompletionItem>>(),
15169        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
15170    );
15171    resolved_items.lock().clear();
15172
15173    cx.update_editor(|editor, window, cx| {
15174        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
15175    });
15176    cx.run_until_parked();
15177    // Completions that have already been resolved are skipped.
15178    assert_eq!(
15179        *resolved_items.lock(),
15180        items[items.len() - 16..items.len() - 4]
15181            .iter()
15182            .cloned()
15183            .map(|mut item| {
15184                if item.data.is_none() {
15185                    item.data = Some(default_data.clone());
15186                }
15187                item
15188            })
15189            .collect::<Vec<lsp::CompletionItem>>()
15190    );
15191    resolved_items.lock().clear();
15192}
15193
15194#[gpui::test]
15195async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
15196    init_test(cx, |_| {});
15197
15198    let mut cx = EditorLspTestContext::new(
15199        Language::new(
15200            LanguageConfig {
15201                matcher: LanguageMatcher {
15202                    path_suffixes: vec!["jsx".into()],
15203                    ..Default::default()
15204                },
15205                overrides: [(
15206                    "element".into(),
15207                    LanguageConfigOverride {
15208                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
15209                        ..Default::default()
15210                    },
15211                )]
15212                .into_iter()
15213                .collect(),
15214                ..Default::default()
15215            },
15216            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
15217        )
15218        .with_override_query("(jsx_self_closing_element) @element")
15219        .unwrap(),
15220        lsp::ServerCapabilities {
15221            completion_provider: Some(lsp::CompletionOptions {
15222                trigger_characters: Some(vec![":".to_string()]),
15223                ..Default::default()
15224            }),
15225            ..Default::default()
15226        },
15227        cx,
15228    )
15229    .await;
15230
15231    cx.lsp
15232        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
15233            Ok(Some(lsp::CompletionResponse::Array(vec![
15234                lsp::CompletionItem {
15235                    label: "bg-blue".into(),
15236                    ..Default::default()
15237                },
15238                lsp::CompletionItem {
15239                    label: "bg-red".into(),
15240                    ..Default::default()
15241                },
15242                lsp::CompletionItem {
15243                    label: "bg-yellow".into(),
15244                    ..Default::default()
15245                },
15246            ])))
15247        });
15248
15249    cx.set_state(r#"<p class="bgˇ" />"#);
15250
15251    // Trigger completion when typing a dash, because the dash is an extra
15252    // word character in the 'element' scope, which contains the cursor.
15253    cx.simulate_keystroke("-");
15254    cx.executor().run_until_parked();
15255    cx.update_editor(|editor, _, _| {
15256        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
15257        {
15258            assert_eq!(
15259                completion_menu_entries(&menu),
15260                &["bg-red", "bg-blue", "bg-yellow"]
15261            );
15262        } else {
15263            panic!("expected completion menu to be open");
15264        }
15265    });
15266
15267    cx.simulate_keystroke("l");
15268    cx.executor().run_until_parked();
15269    cx.update_editor(|editor, _, _| {
15270        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
15271        {
15272            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
15273        } else {
15274            panic!("expected completion menu to be open");
15275        }
15276    });
15277
15278    // When filtering completions, consider the character after the '-' to
15279    // be the start of a subword.
15280    cx.set_state(r#"<p class="yelˇ" />"#);
15281    cx.simulate_keystroke("l");
15282    cx.executor().run_until_parked();
15283    cx.update_editor(|editor, _, _| {
15284        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
15285        {
15286            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
15287        } else {
15288            panic!("expected completion menu to be open");
15289        }
15290    });
15291}
15292
15293fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
15294    let entries = menu.entries.borrow();
15295    entries.iter().map(|mat| mat.string.clone()).collect()
15296}
15297
15298#[gpui::test]
15299async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
15300    init_test(cx, |settings| {
15301        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
15302            FormatterList(vec![Formatter::Prettier].into()),
15303        ))
15304    });
15305
15306    let fs = FakeFs::new(cx.executor());
15307    fs.insert_file(path!("/file.ts"), Default::default()).await;
15308
15309    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
15310    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
15311
15312    language_registry.add(Arc::new(Language::new(
15313        LanguageConfig {
15314            name: "TypeScript".into(),
15315            matcher: LanguageMatcher {
15316                path_suffixes: vec!["ts".to_string()],
15317                ..Default::default()
15318            },
15319            ..Default::default()
15320        },
15321        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
15322    )));
15323    update_test_language_settings(cx, |settings| {
15324        settings.defaults.prettier = Some(PrettierSettings {
15325            allowed: true,
15326            ..PrettierSettings::default()
15327        });
15328    });
15329
15330    let test_plugin = "test_plugin";
15331    let _ = language_registry.register_fake_lsp(
15332        "TypeScript",
15333        FakeLspAdapter {
15334            prettier_plugins: vec![test_plugin],
15335            ..Default::default()
15336        },
15337    );
15338
15339    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
15340    let buffer = project
15341        .update(cx, |project, cx| {
15342            project.open_local_buffer(path!("/file.ts"), cx)
15343        })
15344        .await
15345        .unwrap();
15346
15347    let buffer_text = "one\ntwo\nthree\n";
15348    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
15349    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
15350    editor.update_in(cx, |editor, window, cx| {
15351        editor.set_text(buffer_text, window, cx)
15352    });
15353
15354    editor
15355        .update_in(cx, |editor, window, cx| {
15356            editor.perform_format(
15357                project.clone(),
15358                FormatTrigger::Manual,
15359                FormatTarget::Buffers,
15360                window,
15361                cx,
15362            )
15363        })
15364        .unwrap()
15365        .await;
15366    assert_eq!(
15367        editor.update(cx, |editor, cx| editor.text(cx)),
15368        buffer_text.to_string() + prettier_format_suffix,
15369        "Test prettier formatting was not applied to the original buffer text",
15370    );
15371
15372    update_test_language_settings(cx, |settings| {
15373        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
15374    });
15375    let format = editor.update_in(cx, |editor, window, cx| {
15376        editor.perform_format(
15377            project.clone(),
15378            FormatTrigger::Manual,
15379            FormatTarget::Buffers,
15380            window,
15381            cx,
15382        )
15383    });
15384    format.await.unwrap();
15385    assert_eq!(
15386        editor.update(cx, |editor, cx| editor.text(cx)),
15387        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
15388        "Autoformatting (via test prettier) was not applied to the original buffer text",
15389    );
15390}
15391
15392#[gpui::test]
15393async fn test_addition_reverts(cx: &mut TestAppContext) {
15394    init_test(cx, |_| {});
15395    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15396    let base_text = indoc! {r#"
15397        struct Row;
15398        struct Row1;
15399        struct Row2;
15400
15401        struct Row4;
15402        struct Row5;
15403        struct Row6;
15404
15405        struct Row8;
15406        struct Row9;
15407        struct Row10;"#};
15408
15409    // When addition hunks are not adjacent to carets, no hunk revert is performed
15410    assert_hunk_revert(
15411        indoc! {r#"struct Row;
15412                   struct Row1;
15413                   struct Row1.1;
15414                   struct Row1.2;
15415                   struct Row2;ˇ
15416
15417                   struct Row4;
15418                   struct Row5;
15419                   struct Row6;
15420
15421                   struct Row8;
15422                   ˇstruct Row9;
15423                   struct Row9.1;
15424                   struct Row9.2;
15425                   struct Row9.3;
15426                   struct Row10;"#},
15427        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
15428        indoc! {r#"struct Row;
15429                   struct Row1;
15430                   struct Row1.1;
15431                   struct Row1.2;
15432                   struct Row2;ˇ
15433
15434                   struct Row4;
15435                   struct Row5;
15436                   struct Row6;
15437
15438                   struct Row8;
15439                   ˇstruct Row9;
15440                   struct Row9.1;
15441                   struct Row9.2;
15442                   struct Row9.3;
15443                   struct Row10;"#},
15444        base_text,
15445        &mut cx,
15446    );
15447    // Same for selections
15448    assert_hunk_revert(
15449        indoc! {r#"struct Row;
15450                   struct Row1;
15451                   struct Row2;
15452                   struct Row2.1;
15453                   struct Row2.2;
15454                   «ˇ
15455                   struct Row4;
15456                   struct» Row5;
15457                   «struct Row6;
15458                   ˇ»
15459                   struct Row9.1;
15460                   struct Row9.2;
15461                   struct Row9.3;
15462                   struct Row8;
15463                   struct Row9;
15464                   struct Row10;"#},
15465        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
15466        indoc! {r#"struct Row;
15467                   struct Row1;
15468                   struct Row2;
15469                   struct Row2.1;
15470                   struct Row2.2;
15471                   «ˇ
15472                   struct Row4;
15473                   struct» Row5;
15474                   «struct Row6;
15475                   ˇ»
15476                   struct Row9.1;
15477                   struct Row9.2;
15478                   struct Row9.3;
15479                   struct Row8;
15480                   struct Row9;
15481                   struct Row10;"#},
15482        base_text,
15483        &mut cx,
15484    );
15485
15486    // When carets and selections intersect the addition hunks, those are reverted.
15487    // Adjacent carets got merged.
15488    assert_hunk_revert(
15489        indoc! {r#"struct Row;
15490                   ˇ// something on the top
15491                   struct Row1;
15492                   struct Row2;
15493                   struct Roˇw3.1;
15494                   struct Row2.2;
15495                   struct Row2.3;ˇ
15496
15497                   struct Row4;
15498                   struct ˇRow5.1;
15499                   struct Row5.2;
15500                   struct «Rowˇ»5.3;
15501                   struct Row5;
15502                   struct Row6;
15503                   ˇ
15504                   struct Row9.1;
15505                   struct «Rowˇ»9.2;
15506                   struct «ˇRow»9.3;
15507                   struct Row8;
15508                   struct Row9;
15509                   «ˇ// something on bottom»
15510                   struct Row10;"#},
15511        vec![
15512            DiffHunkStatusKind::Added,
15513            DiffHunkStatusKind::Added,
15514            DiffHunkStatusKind::Added,
15515            DiffHunkStatusKind::Added,
15516            DiffHunkStatusKind::Added,
15517        ],
15518        indoc! {r#"struct Row;
15519                   ˇstruct Row1;
15520                   struct Row2;
15521                   ˇ
15522                   struct Row4;
15523                   ˇstruct Row5;
15524                   struct Row6;
15525                   ˇ
15526                   ˇstruct Row8;
15527                   struct Row9;
15528                   ˇstruct Row10;"#},
15529        base_text,
15530        &mut cx,
15531    );
15532}
15533
15534#[gpui::test]
15535async fn test_modification_reverts(cx: &mut TestAppContext) {
15536    init_test(cx, |_| {});
15537    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15538    let base_text = indoc! {r#"
15539        struct Row;
15540        struct Row1;
15541        struct Row2;
15542
15543        struct Row4;
15544        struct Row5;
15545        struct Row6;
15546
15547        struct Row8;
15548        struct Row9;
15549        struct Row10;"#};
15550
15551    // Modification hunks behave the same as the addition ones.
15552    assert_hunk_revert(
15553        indoc! {r#"struct Row;
15554                   struct Row1;
15555                   struct Row33;
15556                   ˇ
15557                   struct Row4;
15558                   struct Row5;
15559                   struct Row6;
15560                   ˇ
15561                   struct Row99;
15562                   struct Row9;
15563                   struct Row10;"#},
15564        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15565        indoc! {r#"struct Row;
15566                   struct Row1;
15567                   struct Row33;
15568                   ˇ
15569                   struct Row4;
15570                   struct Row5;
15571                   struct Row6;
15572                   ˇ
15573                   struct Row99;
15574                   struct Row9;
15575                   struct Row10;"#},
15576        base_text,
15577        &mut cx,
15578    );
15579    assert_hunk_revert(
15580        indoc! {r#"struct Row;
15581                   struct Row1;
15582                   struct Row33;
15583                   «ˇ
15584                   struct Row4;
15585                   struct» Row5;
15586                   «struct Row6;
15587                   ˇ»
15588                   struct Row99;
15589                   struct Row9;
15590                   struct Row10;"#},
15591        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15592        indoc! {r#"struct Row;
15593                   struct Row1;
15594                   struct Row33;
15595                   «ˇ
15596                   struct Row4;
15597                   struct» Row5;
15598                   «struct Row6;
15599                   ˇ»
15600                   struct Row99;
15601                   struct Row9;
15602                   struct Row10;"#},
15603        base_text,
15604        &mut cx,
15605    );
15606
15607    assert_hunk_revert(
15608        indoc! {r#"ˇstruct Row1.1;
15609                   struct Row1;
15610                   «ˇstr»uct Row22;
15611
15612                   struct ˇRow44;
15613                   struct Row5;
15614                   struct «Rˇ»ow66;ˇ
15615
15616                   «struˇ»ct Row88;
15617                   struct Row9;
15618                   struct Row1011;ˇ"#},
15619        vec![
15620            DiffHunkStatusKind::Modified,
15621            DiffHunkStatusKind::Modified,
15622            DiffHunkStatusKind::Modified,
15623            DiffHunkStatusKind::Modified,
15624            DiffHunkStatusKind::Modified,
15625            DiffHunkStatusKind::Modified,
15626        ],
15627        indoc! {r#"struct Row;
15628                   ˇstruct Row1;
15629                   struct Row2;
15630                   ˇ
15631                   struct Row4;
15632                   ˇstruct Row5;
15633                   struct Row6;
15634                   ˇ
15635                   struct Row8;
15636                   ˇstruct Row9;
15637                   struct Row10;ˇ"#},
15638        base_text,
15639        &mut cx,
15640    );
15641}
15642
15643#[gpui::test]
15644async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
15645    init_test(cx, |_| {});
15646    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15647    let base_text = indoc! {r#"
15648        one
15649
15650        two
15651        three
15652        "#};
15653
15654    cx.set_head_text(base_text);
15655    cx.set_state("\nˇ\n");
15656    cx.executor().run_until_parked();
15657    cx.update_editor(|editor, _window, cx| {
15658        editor.expand_selected_diff_hunks(cx);
15659    });
15660    cx.executor().run_until_parked();
15661    cx.update_editor(|editor, window, cx| {
15662        editor.backspace(&Default::default(), window, cx);
15663    });
15664    cx.run_until_parked();
15665    cx.assert_state_with_diff(
15666        indoc! {r#"
15667
15668        - two
15669        - threeˇ
15670        +
15671        "#}
15672        .to_string(),
15673    );
15674}
15675
15676#[gpui::test]
15677async fn test_deletion_reverts(cx: &mut TestAppContext) {
15678    init_test(cx, |_| {});
15679    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15680    let base_text = indoc! {r#"struct Row;
15681struct Row1;
15682struct Row2;
15683
15684struct Row4;
15685struct Row5;
15686struct Row6;
15687
15688struct Row8;
15689struct Row9;
15690struct Row10;"#};
15691
15692    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
15693    assert_hunk_revert(
15694        indoc! {r#"struct Row;
15695                   struct Row2;
15696
15697                   ˇstruct Row4;
15698                   struct Row5;
15699                   struct Row6;
15700                   ˇ
15701                   struct Row8;
15702                   struct Row10;"#},
15703        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15704        indoc! {r#"struct Row;
15705                   struct Row2;
15706
15707                   ˇstruct Row4;
15708                   struct Row5;
15709                   struct Row6;
15710                   ˇ
15711                   struct Row8;
15712                   struct Row10;"#},
15713        base_text,
15714        &mut cx,
15715    );
15716    assert_hunk_revert(
15717        indoc! {r#"struct Row;
15718                   struct Row2;
15719
15720                   «ˇstruct Row4;
15721                   struct» Row5;
15722                   «struct Row6;
15723                   ˇ»
15724                   struct Row8;
15725                   struct Row10;"#},
15726        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15727        indoc! {r#"struct Row;
15728                   struct Row2;
15729
15730                   «ˇstruct Row4;
15731                   struct» Row5;
15732                   «struct Row6;
15733                   ˇ»
15734                   struct Row8;
15735                   struct Row10;"#},
15736        base_text,
15737        &mut cx,
15738    );
15739
15740    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15741    assert_hunk_revert(
15742        indoc! {r#"struct Row;
15743                   ˇstruct Row2;
15744
15745                   struct Row4;
15746                   struct Row5;
15747                   struct Row6;
15748
15749                   struct Row8;ˇ
15750                   struct Row10;"#},
15751        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15752        indoc! {r#"struct Row;
15753                   struct Row1;
15754                   ˇstruct Row2;
15755
15756                   struct Row4;
15757                   struct Row5;
15758                   struct Row6;
15759
15760                   struct Row8;ˇ
15761                   struct Row9;
15762                   struct Row10;"#},
15763        base_text,
15764        &mut cx,
15765    );
15766    assert_hunk_revert(
15767        indoc! {r#"struct Row;
15768                   struct Row2«ˇ;
15769                   struct Row4;
15770                   struct» Row5;
15771                   «struct Row6;
15772
15773                   struct Row8;ˇ»
15774                   struct Row10;"#},
15775        vec![
15776            DiffHunkStatusKind::Deleted,
15777            DiffHunkStatusKind::Deleted,
15778            DiffHunkStatusKind::Deleted,
15779        ],
15780        indoc! {r#"struct Row;
15781                   struct Row1;
15782                   struct Row2«ˇ;
15783
15784                   struct Row4;
15785                   struct» Row5;
15786                   «struct Row6;
15787
15788                   struct Row8;ˇ»
15789                   struct Row9;
15790                   struct Row10;"#},
15791        base_text,
15792        &mut cx,
15793    );
15794}
15795
15796#[gpui::test]
15797async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15798    init_test(cx, |_| {});
15799
15800    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15801    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15802    let base_text_3 =
15803        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15804
15805    let text_1 = edit_first_char_of_every_line(base_text_1);
15806    let text_2 = edit_first_char_of_every_line(base_text_2);
15807    let text_3 = edit_first_char_of_every_line(base_text_3);
15808
15809    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15810    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15811    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15812
15813    let multibuffer = cx.new(|cx| {
15814        let mut multibuffer = MultiBuffer::new(ReadWrite);
15815        multibuffer.push_excerpts(
15816            buffer_1.clone(),
15817            [
15818                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15819                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15820                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15821            ],
15822            cx,
15823        );
15824        multibuffer.push_excerpts(
15825            buffer_2.clone(),
15826            [
15827                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15828                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15829                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15830            ],
15831            cx,
15832        );
15833        multibuffer.push_excerpts(
15834            buffer_3.clone(),
15835            [
15836                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15837                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15838                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15839            ],
15840            cx,
15841        );
15842        multibuffer
15843    });
15844
15845    let fs = FakeFs::new(cx.executor());
15846    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15847    let (editor, cx) = cx
15848        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15849    editor.update_in(cx, |editor, _window, cx| {
15850        for (buffer, diff_base) in [
15851            (buffer_1.clone(), base_text_1),
15852            (buffer_2.clone(), base_text_2),
15853            (buffer_3.clone(), base_text_3),
15854        ] {
15855            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15856            editor
15857                .buffer
15858                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15859        }
15860    });
15861    cx.executor().run_until_parked();
15862
15863    editor.update_in(cx, |editor, window, cx| {
15864        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}");
15865        editor.select_all(&SelectAll, window, cx);
15866        editor.git_restore(&Default::default(), window, cx);
15867    });
15868    cx.executor().run_until_parked();
15869
15870    // When all ranges are selected, all buffer hunks are reverted.
15871    editor.update(cx, |editor, cx| {
15872        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");
15873    });
15874    buffer_1.update(cx, |buffer, _| {
15875        assert_eq!(buffer.text(), base_text_1);
15876    });
15877    buffer_2.update(cx, |buffer, _| {
15878        assert_eq!(buffer.text(), base_text_2);
15879    });
15880    buffer_3.update(cx, |buffer, _| {
15881        assert_eq!(buffer.text(), base_text_3);
15882    });
15883
15884    editor.update_in(cx, |editor, window, cx| {
15885        editor.undo(&Default::default(), window, cx);
15886    });
15887
15888    editor.update_in(cx, |editor, window, cx| {
15889        editor.change_selections(None, window, cx, |s| {
15890            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15891        });
15892        editor.git_restore(&Default::default(), window, cx);
15893    });
15894
15895    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15896    // but not affect buffer_2 and its related excerpts.
15897    editor.update(cx, |editor, cx| {
15898        assert_eq!(
15899            editor.text(cx),
15900            "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}"
15901        );
15902    });
15903    buffer_1.update(cx, |buffer, _| {
15904        assert_eq!(buffer.text(), base_text_1);
15905    });
15906    buffer_2.update(cx, |buffer, _| {
15907        assert_eq!(
15908            buffer.text(),
15909            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15910        );
15911    });
15912    buffer_3.update(cx, |buffer, _| {
15913        assert_eq!(
15914            buffer.text(),
15915            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15916        );
15917    });
15918
15919    fn edit_first_char_of_every_line(text: &str) -> String {
15920        text.split('\n')
15921            .map(|line| format!("X{}", &line[1..]))
15922            .collect::<Vec<_>>()
15923            .join("\n")
15924    }
15925}
15926
15927#[gpui::test]
15928async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15929    init_test(cx, |_| {});
15930
15931    let cols = 4;
15932    let rows = 10;
15933    let sample_text_1 = sample_text(rows, cols, 'a');
15934    assert_eq!(
15935        sample_text_1,
15936        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15937    );
15938    let sample_text_2 = sample_text(rows, cols, 'l');
15939    assert_eq!(
15940        sample_text_2,
15941        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15942    );
15943    let sample_text_3 = sample_text(rows, cols, 'v');
15944    assert_eq!(
15945        sample_text_3,
15946        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15947    );
15948
15949    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15950    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15951    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15952
15953    let multi_buffer = cx.new(|cx| {
15954        let mut multibuffer = MultiBuffer::new(ReadWrite);
15955        multibuffer.push_excerpts(
15956            buffer_1.clone(),
15957            [
15958                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15959                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15960                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15961            ],
15962            cx,
15963        );
15964        multibuffer.push_excerpts(
15965            buffer_2.clone(),
15966            [
15967                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15968                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15969                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15970            ],
15971            cx,
15972        );
15973        multibuffer.push_excerpts(
15974            buffer_3.clone(),
15975            [
15976                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15977                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15978                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15979            ],
15980            cx,
15981        );
15982        multibuffer
15983    });
15984
15985    let fs = FakeFs::new(cx.executor());
15986    fs.insert_tree(
15987        "/a",
15988        json!({
15989            "main.rs": sample_text_1,
15990            "other.rs": sample_text_2,
15991            "lib.rs": sample_text_3,
15992        }),
15993    )
15994    .await;
15995    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15996    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15997    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15998    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15999        Editor::new(
16000            EditorMode::full(),
16001            multi_buffer,
16002            Some(project.clone()),
16003            window,
16004            cx,
16005        )
16006    });
16007    let multibuffer_item_id = workspace
16008        .update(cx, |workspace, window, cx| {
16009            assert!(
16010                workspace.active_item(cx).is_none(),
16011                "active item should be None before the first item is added"
16012            );
16013            workspace.add_item_to_active_pane(
16014                Box::new(multi_buffer_editor.clone()),
16015                None,
16016                true,
16017                window,
16018                cx,
16019            );
16020            let active_item = workspace
16021                .active_item(cx)
16022                .expect("should have an active item after adding the multi buffer");
16023            assert!(
16024                !active_item.is_singleton(cx),
16025                "A multi buffer was expected to active after adding"
16026            );
16027            active_item.item_id()
16028        })
16029        .unwrap();
16030    cx.executor().run_until_parked();
16031
16032    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16033        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
16034            s.select_ranges(Some(1..2))
16035        });
16036        editor.open_excerpts(&OpenExcerpts, window, cx);
16037    });
16038    cx.executor().run_until_parked();
16039    let first_item_id = workspace
16040        .update(cx, |workspace, window, cx| {
16041            let active_item = workspace
16042                .active_item(cx)
16043                .expect("should have an active item after navigating into the 1st buffer");
16044            let first_item_id = active_item.item_id();
16045            assert_ne!(
16046                first_item_id, multibuffer_item_id,
16047                "Should navigate into the 1st buffer and activate it"
16048            );
16049            assert!(
16050                active_item.is_singleton(cx),
16051                "New active item should be a singleton buffer"
16052            );
16053            assert_eq!(
16054                active_item
16055                    .act_as::<Editor>(cx)
16056                    .expect("should have navigated into an editor for the 1st buffer")
16057                    .read(cx)
16058                    .text(cx),
16059                sample_text_1
16060            );
16061
16062            workspace
16063                .go_back(workspace.active_pane().downgrade(), window, cx)
16064                .detach_and_log_err(cx);
16065
16066            first_item_id
16067        })
16068        .unwrap();
16069    cx.executor().run_until_parked();
16070    workspace
16071        .update(cx, |workspace, _, cx| {
16072            let active_item = workspace
16073                .active_item(cx)
16074                .expect("should have an active item after navigating back");
16075            assert_eq!(
16076                active_item.item_id(),
16077                multibuffer_item_id,
16078                "Should navigate back to the multi buffer"
16079            );
16080            assert!(!active_item.is_singleton(cx));
16081        })
16082        .unwrap();
16083
16084    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16085        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
16086            s.select_ranges(Some(39..40))
16087        });
16088        editor.open_excerpts(&OpenExcerpts, window, cx);
16089    });
16090    cx.executor().run_until_parked();
16091    let second_item_id = workspace
16092        .update(cx, |workspace, window, cx| {
16093            let active_item = workspace
16094                .active_item(cx)
16095                .expect("should have an active item after navigating into the 2nd buffer");
16096            let second_item_id = active_item.item_id();
16097            assert_ne!(
16098                second_item_id, multibuffer_item_id,
16099                "Should navigate away from the multibuffer"
16100            );
16101            assert_ne!(
16102                second_item_id, first_item_id,
16103                "Should navigate into the 2nd buffer and activate it"
16104            );
16105            assert!(
16106                active_item.is_singleton(cx),
16107                "New active item should be a singleton buffer"
16108            );
16109            assert_eq!(
16110                active_item
16111                    .act_as::<Editor>(cx)
16112                    .expect("should have navigated into an editor")
16113                    .read(cx)
16114                    .text(cx),
16115                sample_text_2
16116            );
16117
16118            workspace
16119                .go_back(workspace.active_pane().downgrade(), window, cx)
16120                .detach_and_log_err(cx);
16121
16122            second_item_id
16123        })
16124        .unwrap();
16125    cx.executor().run_until_parked();
16126    workspace
16127        .update(cx, |workspace, _, cx| {
16128            let active_item = workspace
16129                .active_item(cx)
16130                .expect("should have an active item after navigating back from the 2nd buffer");
16131            assert_eq!(
16132                active_item.item_id(),
16133                multibuffer_item_id,
16134                "Should navigate back from the 2nd buffer to the multi buffer"
16135            );
16136            assert!(!active_item.is_singleton(cx));
16137        })
16138        .unwrap();
16139
16140    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16141        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
16142            s.select_ranges(Some(70..70))
16143        });
16144        editor.open_excerpts(&OpenExcerpts, window, cx);
16145    });
16146    cx.executor().run_until_parked();
16147    workspace
16148        .update(cx, |workspace, window, cx| {
16149            let active_item = workspace
16150                .active_item(cx)
16151                .expect("should have an active item after navigating into the 3rd buffer");
16152            let third_item_id = active_item.item_id();
16153            assert_ne!(
16154                third_item_id, multibuffer_item_id,
16155                "Should navigate into the 3rd buffer and activate it"
16156            );
16157            assert_ne!(third_item_id, first_item_id);
16158            assert_ne!(third_item_id, second_item_id);
16159            assert!(
16160                active_item.is_singleton(cx),
16161                "New active item should be a singleton buffer"
16162            );
16163            assert_eq!(
16164                active_item
16165                    .act_as::<Editor>(cx)
16166                    .expect("should have navigated into an editor")
16167                    .read(cx)
16168                    .text(cx),
16169                sample_text_3
16170            );
16171
16172            workspace
16173                .go_back(workspace.active_pane().downgrade(), window, cx)
16174                .detach_and_log_err(cx);
16175        })
16176        .unwrap();
16177    cx.executor().run_until_parked();
16178    workspace
16179        .update(cx, |workspace, _, cx| {
16180            let active_item = workspace
16181                .active_item(cx)
16182                .expect("should have an active item after navigating back from the 3rd buffer");
16183            assert_eq!(
16184                active_item.item_id(),
16185                multibuffer_item_id,
16186                "Should navigate back from the 3rd buffer to the multi buffer"
16187            );
16188            assert!(!active_item.is_singleton(cx));
16189        })
16190        .unwrap();
16191}
16192
16193#[gpui::test]
16194async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16195    init_test(cx, |_| {});
16196
16197    let mut cx = EditorTestContext::new(cx).await;
16198
16199    let diff_base = r#"
16200        use some::mod;
16201
16202        const A: u32 = 42;
16203
16204        fn main() {
16205            println!("hello");
16206
16207            println!("world");
16208        }
16209        "#
16210    .unindent();
16211
16212    cx.set_state(
16213        &r#"
16214        use some::modified;
16215
16216        ˇ
16217        fn main() {
16218            println!("hello there");
16219
16220            println!("around the");
16221            println!("world");
16222        }
16223        "#
16224        .unindent(),
16225    );
16226
16227    cx.set_head_text(&diff_base);
16228    executor.run_until_parked();
16229
16230    cx.update_editor(|editor, window, cx| {
16231        editor.go_to_next_hunk(&GoToHunk, window, cx);
16232        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
16233    });
16234    executor.run_until_parked();
16235    cx.assert_state_with_diff(
16236        r#"
16237          use some::modified;
16238
16239
16240          fn main() {
16241        -     println!("hello");
16242        + ˇ    println!("hello there");
16243
16244              println!("around the");
16245              println!("world");
16246          }
16247        "#
16248        .unindent(),
16249    );
16250
16251    cx.update_editor(|editor, window, cx| {
16252        for _ in 0..2 {
16253            editor.go_to_next_hunk(&GoToHunk, window, cx);
16254            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
16255        }
16256    });
16257    executor.run_until_parked();
16258    cx.assert_state_with_diff(
16259        r#"
16260        - use some::mod;
16261        + ˇuse some::modified;
16262
16263
16264          fn main() {
16265        -     println!("hello");
16266        +     println!("hello there");
16267
16268        +     println!("around the");
16269              println!("world");
16270          }
16271        "#
16272        .unindent(),
16273    );
16274
16275    cx.update_editor(|editor, window, cx| {
16276        editor.go_to_next_hunk(&GoToHunk, window, cx);
16277        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
16278    });
16279    executor.run_until_parked();
16280    cx.assert_state_with_diff(
16281        r#"
16282        - use some::mod;
16283        + use some::modified;
16284
16285        - const A: u32 = 42;
16286          ˇ
16287          fn main() {
16288        -     println!("hello");
16289        +     println!("hello there");
16290
16291        +     println!("around the");
16292              println!("world");
16293          }
16294        "#
16295        .unindent(),
16296    );
16297
16298    cx.update_editor(|editor, window, cx| {
16299        editor.cancel(&Cancel, window, cx);
16300    });
16301
16302    cx.assert_state_with_diff(
16303        r#"
16304          use some::modified;
16305
16306          ˇ
16307          fn main() {
16308              println!("hello there");
16309
16310              println!("around the");
16311              println!("world");
16312          }
16313        "#
16314        .unindent(),
16315    );
16316}
16317
16318#[gpui::test]
16319async fn test_diff_base_change_with_expanded_diff_hunks(
16320    executor: BackgroundExecutor,
16321    cx: &mut TestAppContext,
16322) {
16323    init_test(cx, |_| {});
16324
16325    let mut cx = EditorTestContext::new(cx).await;
16326
16327    let diff_base = r#"
16328        use some::mod1;
16329        use some::mod2;
16330
16331        const A: u32 = 42;
16332        const B: u32 = 42;
16333        const C: u32 = 42;
16334
16335        fn main() {
16336            println!("hello");
16337
16338            println!("world");
16339        }
16340        "#
16341    .unindent();
16342
16343    cx.set_state(
16344        &r#"
16345        use some::mod2;
16346
16347        const A: u32 = 42;
16348        const C: u32 = 42;
16349
16350        fn main(ˇ) {
16351            //println!("hello");
16352
16353            println!("world");
16354            //
16355            //
16356        }
16357        "#
16358        .unindent(),
16359    );
16360
16361    cx.set_head_text(&diff_base);
16362    executor.run_until_parked();
16363
16364    cx.update_editor(|editor, window, cx| {
16365        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16366    });
16367    executor.run_until_parked();
16368    cx.assert_state_with_diff(
16369        r#"
16370        - use some::mod1;
16371          use some::mod2;
16372
16373          const A: u32 = 42;
16374        - const B: u32 = 42;
16375          const C: u32 = 42;
16376
16377          fn main(ˇ) {
16378        -     println!("hello");
16379        +     //println!("hello");
16380
16381              println!("world");
16382        +     //
16383        +     //
16384          }
16385        "#
16386        .unindent(),
16387    );
16388
16389    cx.set_head_text("new diff base!");
16390    executor.run_until_parked();
16391    cx.assert_state_with_diff(
16392        r#"
16393        - new diff base!
16394        + use some::mod2;
16395        +
16396        + const A: u32 = 42;
16397        + const C: u32 = 42;
16398        +
16399        + fn main(ˇ) {
16400        +     //println!("hello");
16401        +
16402        +     println!("world");
16403        +     //
16404        +     //
16405        + }
16406        "#
16407        .unindent(),
16408    );
16409}
16410
16411#[gpui::test]
16412async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
16413    init_test(cx, |_| {});
16414
16415    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
16416    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
16417    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
16418    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
16419    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
16420    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
16421
16422    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
16423    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
16424    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
16425
16426    let multi_buffer = cx.new(|cx| {
16427        let mut multibuffer = MultiBuffer::new(ReadWrite);
16428        multibuffer.push_excerpts(
16429            buffer_1.clone(),
16430            [
16431                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16432                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16433                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
16434            ],
16435            cx,
16436        );
16437        multibuffer.push_excerpts(
16438            buffer_2.clone(),
16439            [
16440                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16441                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16442                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
16443            ],
16444            cx,
16445        );
16446        multibuffer.push_excerpts(
16447            buffer_3.clone(),
16448            [
16449                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16450                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16451                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
16452            ],
16453            cx,
16454        );
16455        multibuffer
16456    });
16457
16458    let editor =
16459        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
16460    editor
16461        .update(cx, |editor, _window, cx| {
16462            for (buffer, diff_base) in [
16463                (buffer_1.clone(), file_1_old),
16464                (buffer_2.clone(), file_2_old),
16465                (buffer_3.clone(), file_3_old),
16466            ] {
16467                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
16468                editor
16469                    .buffer
16470                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
16471            }
16472        })
16473        .unwrap();
16474
16475    let mut cx = EditorTestContext::for_editor(editor, cx).await;
16476    cx.run_until_parked();
16477
16478    cx.assert_editor_state(
16479        &"
16480            ˇaaa
16481            ccc
16482            ddd
16483
16484            ggg
16485            hhh
16486
16487
16488            lll
16489            mmm
16490            NNN
16491
16492            qqq
16493            rrr
16494
16495            uuu
16496            111
16497            222
16498            333
16499
16500            666
16501            777
16502
16503            000
16504            !!!"
16505        .unindent(),
16506    );
16507
16508    cx.update_editor(|editor, window, cx| {
16509        editor.select_all(&SelectAll, window, cx);
16510        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
16511    });
16512    cx.executor().run_until_parked();
16513
16514    cx.assert_state_with_diff(
16515        "
16516            «aaa
16517          - bbb
16518            ccc
16519            ddd
16520
16521            ggg
16522            hhh
16523
16524
16525            lll
16526            mmm
16527          - nnn
16528          + NNN
16529
16530            qqq
16531            rrr
16532
16533            uuu
16534            111
16535            222
16536            333
16537
16538          + 666
16539            777
16540
16541            000
16542            !!!ˇ»"
16543            .unindent(),
16544    );
16545}
16546
16547#[gpui::test]
16548async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
16549    init_test(cx, |_| {});
16550
16551    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
16552    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
16553
16554    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
16555    let multi_buffer = cx.new(|cx| {
16556        let mut multibuffer = MultiBuffer::new(ReadWrite);
16557        multibuffer.push_excerpts(
16558            buffer.clone(),
16559            [
16560                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
16561                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
16562                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
16563            ],
16564            cx,
16565        );
16566        multibuffer
16567    });
16568
16569    let editor =
16570        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
16571    editor
16572        .update(cx, |editor, _window, cx| {
16573            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
16574            editor
16575                .buffer
16576                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
16577        })
16578        .unwrap();
16579
16580    let mut cx = EditorTestContext::for_editor(editor, cx).await;
16581    cx.run_until_parked();
16582
16583    cx.update_editor(|editor, window, cx| {
16584        editor.expand_all_diff_hunks(&Default::default(), window, cx)
16585    });
16586    cx.executor().run_until_parked();
16587
16588    // When the start of a hunk coincides with the start of its excerpt,
16589    // the hunk is expanded. When the start of a a hunk is earlier than
16590    // the start of its excerpt, the hunk is not expanded.
16591    cx.assert_state_with_diff(
16592        "
16593            ˇaaa
16594          - bbb
16595          + BBB
16596
16597          - ddd
16598          - eee
16599          + DDD
16600          + EEE
16601            fff
16602
16603            iii
16604        "
16605        .unindent(),
16606    );
16607}
16608
16609#[gpui::test]
16610async fn test_edits_around_expanded_insertion_hunks(
16611    executor: BackgroundExecutor,
16612    cx: &mut TestAppContext,
16613) {
16614    init_test(cx, |_| {});
16615
16616    let mut cx = EditorTestContext::new(cx).await;
16617
16618    let diff_base = r#"
16619        use some::mod1;
16620        use some::mod2;
16621
16622        const A: u32 = 42;
16623
16624        fn main() {
16625            println!("hello");
16626
16627            println!("world");
16628        }
16629        "#
16630    .unindent();
16631    executor.run_until_parked();
16632    cx.set_state(
16633        &r#"
16634        use some::mod1;
16635        use some::mod2;
16636
16637        const A: u32 = 42;
16638        const B: u32 = 42;
16639        const C: u32 = 42;
16640        ˇ
16641
16642        fn main() {
16643            println!("hello");
16644
16645            println!("world");
16646        }
16647        "#
16648        .unindent(),
16649    );
16650
16651    cx.set_head_text(&diff_base);
16652    executor.run_until_parked();
16653
16654    cx.update_editor(|editor, window, cx| {
16655        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16656    });
16657    executor.run_until_parked();
16658
16659    cx.assert_state_with_diff(
16660        r#"
16661        use some::mod1;
16662        use some::mod2;
16663
16664        const A: u32 = 42;
16665      + const B: u32 = 42;
16666      + const C: u32 = 42;
16667      + ˇ
16668
16669        fn main() {
16670            println!("hello");
16671
16672            println!("world");
16673        }
16674      "#
16675        .unindent(),
16676    );
16677
16678    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
16679    executor.run_until_parked();
16680
16681    cx.assert_state_with_diff(
16682        r#"
16683        use some::mod1;
16684        use some::mod2;
16685
16686        const A: u32 = 42;
16687      + const B: u32 = 42;
16688      + const C: u32 = 42;
16689      + const D: u32 = 42;
16690      + ˇ
16691
16692        fn main() {
16693            println!("hello");
16694
16695            println!("world");
16696        }
16697      "#
16698        .unindent(),
16699    );
16700
16701    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
16702    executor.run_until_parked();
16703
16704    cx.assert_state_with_diff(
16705        r#"
16706        use some::mod1;
16707        use some::mod2;
16708
16709        const A: u32 = 42;
16710      + const B: u32 = 42;
16711      + const C: u32 = 42;
16712      + const D: u32 = 42;
16713      + const E: u32 = 42;
16714      + ˇ
16715
16716        fn main() {
16717            println!("hello");
16718
16719            println!("world");
16720        }
16721      "#
16722        .unindent(),
16723    );
16724
16725    cx.update_editor(|editor, window, cx| {
16726        editor.delete_line(&DeleteLine, window, cx);
16727    });
16728    executor.run_until_parked();
16729
16730    cx.assert_state_with_diff(
16731        r#"
16732        use some::mod1;
16733        use some::mod2;
16734
16735        const A: u32 = 42;
16736      + const B: u32 = 42;
16737      + const C: u32 = 42;
16738      + const D: u32 = 42;
16739      + const E: u32 = 42;
16740        ˇ
16741        fn main() {
16742            println!("hello");
16743
16744            println!("world");
16745        }
16746      "#
16747        .unindent(),
16748    );
16749
16750    cx.update_editor(|editor, window, cx| {
16751        editor.move_up(&MoveUp, window, cx);
16752        editor.delete_line(&DeleteLine, window, cx);
16753        editor.move_up(&MoveUp, window, cx);
16754        editor.delete_line(&DeleteLine, window, cx);
16755        editor.move_up(&MoveUp, window, cx);
16756        editor.delete_line(&DeleteLine, window, cx);
16757    });
16758    executor.run_until_parked();
16759    cx.assert_state_with_diff(
16760        r#"
16761        use some::mod1;
16762        use some::mod2;
16763
16764        const A: u32 = 42;
16765      + const B: u32 = 42;
16766        ˇ
16767        fn main() {
16768            println!("hello");
16769
16770            println!("world");
16771        }
16772      "#
16773        .unindent(),
16774    );
16775
16776    cx.update_editor(|editor, window, cx| {
16777        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16778        editor.delete_line(&DeleteLine, window, cx);
16779    });
16780    executor.run_until_parked();
16781    cx.assert_state_with_diff(
16782        r#"
16783        ˇ
16784        fn main() {
16785            println!("hello");
16786
16787            println!("world");
16788        }
16789      "#
16790        .unindent(),
16791    );
16792}
16793
16794#[gpui::test]
16795async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16796    init_test(cx, |_| {});
16797
16798    let mut cx = EditorTestContext::new(cx).await;
16799    cx.set_head_text(indoc! { "
16800        one
16801        two
16802        three
16803        four
16804        five
16805        "
16806    });
16807    cx.set_state(indoc! { "
16808        one
16809        ˇthree
16810        five
16811    "});
16812    cx.run_until_parked();
16813    cx.update_editor(|editor, window, cx| {
16814        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16815    });
16816    cx.assert_state_with_diff(
16817        indoc! { "
16818        one
16819      - two
16820        ˇthree
16821      - four
16822        five
16823    "}
16824        .to_string(),
16825    );
16826    cx.update_editor(|editor, window, cx| {
16827        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16828    });
16829
16830    cx.assert_state_with_diff(
16831        indoc! { "
16832        one
16833        ˇthree
16834        five
16835    "}
16836        .to_string(),
16837    );
16838
16839    cx.set_state(indoc! { "
16840        one
16841        ˇTWO
16842        three
16843        four
16844        five
16845    "});
16846    cx.run_until_parked();
16847    cx.update_editor(|editor, window, cx| {
16848        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16849    });
16850
16851    cx.assert_state_with_diff(
16852        indoc! { "
16853            one
16854          - two
16855          + ˇTWO
16856            three
16857            four
16858            five
16859        "}
16860        .to_string(),
16861    );
16862    cx.update_editor(|editor, window, cx| {
16863        editor.move_up(&Default::default(), window, cx);
16864        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16865    });
16866    cx.assert_state_with_diff(
16867        indoc! { "
16868            one
16869            ˇTWO
16870            three
16871            four
16872            five
16873        "}
16874        .to_string(),
16875    );
16876}
16877
16878#[gpui::test]
16879async fn test_edits_around_expanded_deletion_hunks(
16880    executor: BackgroundExecutor,
16881    cx: &mut TestAppContext,
16882) {
16883    init_test(cx, |_| {});
16884
16885    let mut cx = EditorTestContext::new(cx).await;
16886
16887    let diff_base = r#"
16888        use some::mod1;
16889        use some::mod2;
16890
16891        const A: u32 = 42;
16892        const B: u32 = 42;
16893        const C: u32 = 42;
16894
16895
16896        fn main() {
16897            println!("hello");
16898
16899            println!("world");
16900        }
16901    "#
16902    .unindent();
16903    executor.run_until_parked();
16904    cx.set_state(
16905        &r#"
16906        use some::mod1;
16907        use some::mod2;
16908
16909        ˇconst B: u32 = 42;
16910        const C: u32 = 42;
16911
16912
16913        fn main() {
16914            println!("hello");
16915
16916            println!("world");
16917        }
16918        "#
16919        .unindent(),
16920    );
16921
16922    cx.set_head_text(&diff_base);
16923    executor.run_until_parked();
16924
16925    cx.update_editor(|editor, window, cx| {
16926        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16927    });
16928    executor.run_until_parked();
16929
16930    cx.assert_state_with_diff(
16931        r#"
16932        use some::mod1;
16933        use some::mod2;
16934
16935      - const A: u32 = 42;
16936        ˇconst B: u32 = 42;
16937        const C: u32 = 42;
16938
16939
16940        fn main() {
16941            println!("hello");
16942
16943            println!("world");
16944        }
16945      "#
16946        .unindent(),
16947    );
16948
16949    cx.update_editor(|editor, window, cx| {
16950        editor.delete_line(&DeleteLine, window, cx);
16951    });
16952    executor.run_until_parked();
16953    cx.assert_state_with_diff(
16954        r#"
16955        use some::mod1;
16956        use some::mod2;
16957
16958      - const A: u32 = 42;
16959      - const B: u32 = 42;
16960        ˇconst C: u32 = 42;
16961
16962
16963        fn main() {
16964            println!("hello");
16965
16966            println!("world");
16967        }
16968      "#
16969        .unindent(),
16970    );
16971
16972    cx.update_editor(|editor, window, cx| {
16973        editor.delete_line(&DeleteLine, window, cx);
16974    });
16975    executor.run_until_parked();
16976    cx.assert_state_with_diff(
16977        r#"
16978        use some::mod1;
16979        use some::mod2;
16980
16981      - const A: u32 = 42;
16982      - const B: u32 = 42;
16983      - const C: u32 = 42;
16984        ˇ
16985
16986        fn main() {
16987            println!("hello");
16988
16989            println!("world");
16990        }
16991      "#
16992        .unindent(),
16993    );
16994
16995    cx.update_editor(|editor, window, cx| {
16996        editor.handle_input("replacement", window, cx);
16997    });
16998    executor.run_until_parked();
16999    cx.assert_state_with_diff(
17000        r#"
17001        use some::mod1;
17002        use some::mod2;
17003
17004      - const A: u32 = 42;
17005      - const B: u32 = 42;
17006      - const C: u32 = 42;
17007      -
17008      + replacementˇ
17009
17010        fn main() {
17011            println!("hello");
17012
17013            println!("world");
17014        }
17015      "#
17016        .unindent(),
17017    );
17018}
17019
17020#[gpui::test]
17021async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17022    init_test(cx, |_| {});
17023
17024    let mut cx = EditorTestContext::new(cx).await;
17025
17026    let base_text = r#"
17027        one
17028        two
17029        three
17030        four
17031        five
17032    "#
17033    .unindent();
17034    executor.run_until_parked();
17035    cx.set_state(
17036        &r#"
17037        one
17038        two
17039        fˇour
17040        five
17041        "#
17042        .unindent(),
17043    );
17044
17045    cx.set_head_text(&base_text);
17046    executor.run_until_parked();
17047
17048    cx.update_editor(|editor, window, cx| {
17049        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17050    });
17051    executor.run_until_parked();
17052
17053    cx.assert_state_with_diff(
17054        r#"
17055          one
17056          two
17057        - three
17058          fˇour
17059          five
17060        "#
17061        .unindent(),
17062    );
17063
17064    cx.update_editor(|editor, window, cx| {
17065        editor.backspace(&Backspace, window, cx);
17066        editor.backspace(&Backspace, window, cx);
17067    });
17068    executor.run_until_parked();
17069    cx.assert_state_with_diff(
17070        r#"
17071          one
17072          two
17073        - threeˇ
17074        - four
17075        + our
17076          five
17077        "#
17078        .unindent(),
17079    );
17080}
17081
17082#[gpui::test]
17083async fn test_edit_after_expanded_modification_hunk(
17084    executor: BackgroundExecutor,
17085    cx: &mut TestAppContext,
17086) {
17087    init_test(cx, |_| {});
17088
17089    let mut cx = EditorTestContext::new(cx).await;
17090
17091    let diff_base = r#"
17092        use some::mod1;
17093        use some::mod2;
17094
17095        const A: u32 = 42;
17096        const B: u32 = 42;
17097        const C: u32 = 42;
17098        const D: u32 = 42;
17099
17100
17101        fn main() {
17102            println!("hello");
17103
17104            println!("world");
17105        }"#
17106    .unindent();
17107
17108    cx.set_state(
17109        &r#"
17110        use some::mod1;
17111        use some::mod2;
17112
17113        const A: u32 = 42;
17114        const B: u32 = 42;
17115        const C: u32 = 43ˇ
17116        const D: u32 = 42;
17117
17118
17119        fn main() {
17120            println!("hello");
17121
17122            println!("world");
17123        }"#
17124        .unindent(),
17125    );
17126
17127    cx.set_head_text(&diff_base);
17128    executor.run_until_parked();
17129    cx.update_editor(|editor, window, cx| {
17130        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17131    });
17132    executor.run_until_parked();
17133
17134    cx.assert_state_with_diff(
17135        r#"
17136        use some::mod1;
17137        use some::mod2;
17138
17139        const A: u32 = 42;
17140        const B: u32 = 42;
17141      - const C: u32 = 42;
17142      + const C: u32 = 43ˇ
17143        const D: u32 = 42;
17144
17145
17146        fn main() {
17147            println!("hello");
17148
17149            println!("world");
17150        }"#
17151        .unindent(),
17152    );
17153
17154    cx.update_editor(|editor, window, cx| {
17155        editor.handle_input("\nnew_line\n", window, cx);
17156    });
17157    executor.run_until_parked();
17158
17159    cx.assert_state_with_diff(
17160        r#"
17161        use some::mod1;
17162        use some::mod2;
17163
17164        const A: u32 = 42;
17165        const B: u32 = 42;
17166      - const C: u32 = 42;
17167      + const C: u32 = 43
17168      + new_line
17169      + ˇ
17170        const D: u32 = 42;
17171
17172
17173        fn main() {
17174            println!("hello");
17175
17176            println!("world");
17177        }"#
17178        .unindent(),
17179    );
17180}
17181
17182#[gpui::test]
17183async fn test_stage_and_unstage_added_file_hunk(
17184    executor: BackgroundExecutor,
17185    cx: &mut TestAppContext,
17186) {
17187    init_test(cx, |_| {});
17188
17189    let mut cx = EditorTestContext::new(cx).await;
17190    cx.update_editor(|editor, _, cx| {
17191        editor.set_expand_all_diff_hunks(cx);
17192    });
17193
17194    let working_copy = r#"
17195            ˇfn main() {
17196                println!("hello, world!");
17197            }
17198        "#
17199    .unindent();
17200
17201    cx.set_state(&working_copy);
17202    executor.run_until_parked();
17203
17204    cx.assert_state_with_diff(
17205        r#"
17206            + ˇfn main() {
17207            +     println!("hello, world!");
17208            + }
17209        "#
17210        .unindent(),
17211    );
17212    cx.assert_index_text(None);
17213
17214    cx.update_editor(|editor, window, cx| {
17215        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17216    });
17217    executor.run_until_parked();
17218    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
17219    cx.assert_state_with_diff(
17220        r#"
17221            + ˇfn main() {
17222            +     println!("hello, world!");
17223            + }
17224        "#
17225        .unindent(),
17226    );
17227
17228    cx.update_editor(|editor, window, cx| {
17229        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17230    });
17231    executor.run_until_parked();
17232    cx.assert_index_text(None);
17233}
17234
17235async fn setup_indent_guides_editor(
17236    text: &str,
17237    cx: &mut TestAppContext,
17238) -> (BufferId, EditorTestContext) {
17239    init_test(cx, |_| {});
17240
17241    let mut cx = EditorTestContext::new(cx).await;
17242
17243    let buffer_id = cx.update_editor(|editor, window, cx| {
17244        editor.set_text(text, window, cx);
17245        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
17246
17247        buffer_ids[0]
17248    });
17249
17250    (buffer_id, cx)
17251}
17252
17253fn assert_indent_guides(
17254    range: Range<u32>,
17255    expected: Vec<IndentGuide>,
17256    active_indices: Option<Vec<usize>>,
17257    cx: &mut EditorTestContext,
17258) {
17259    let indent_guides = cx.update_editor(|editor, window, cx| {
17260        let snapshot = editor.snapshot(window, cx).display_snapshot;
17261        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
17262            editor,
17263            MultiBufferRow(range.start)..MultiBufferRow(range.end),
17264            true,
17265            &snapshot,
17266            cx,
17267        );
17268
17269        indent_guides.sort_by(|a, b| {
17270            a.depth.cmp(&b.depth).then(
17271                a.start_row
17272                    .cmp(&b.start_row)
17273                    .then(a.end_row.cmp(&b.end_row)),
17274            )
17275        });
17276        indent_guides
17277    });
17278
17279    if let Some(expected) = active_indices {
17280        let active_indices = cx.update_editor(|editor, window, cx| {
17281            let snapshot = editor.snapshot(window, cx).display_snapshot;
17282            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
17283        });
17284
17285        assert_eq!(
17286            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
17287            expected,
17288            "Active indent guide indices do not match"
17289        );
17290    }
17291
17292    assert_eq!(indent_guides, expected, "Indent guides do not match");
17293}
17294
17295fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
17296    IndentGuide {
17297        buffer_id,
17298        start_row: MultiBufferRow(start_row),
17299        end_row: MultiBufferRow(end_row),
17300        depth,
17301        tab_size: 4,
17302        settings: IndentGuideSettings {
17303            enabled: true,
17304            line_width: 1,
17305            active_line_width: 1,
17306            ..Default::default()
17307        },
17308    }
17309}
17310
17311#[gpui::test]
17312async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
17313    let (buffer_id, mut cx) = setup_indent_guides_editor(
17314        &"
17315        fn main() {
17316            let a = 1;
17317        }"
17318        .unindent(),
17319        cx,
17320    )
17321    .await;
17322
17323    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
17324}
17325
17326#[gpui::test]
17327async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
17328    let (buffer_id, mut cx) = setup_indent_guides_editor(
17329        &"
17330        fn main() {
17331            let a = 1;
17332            let b = 2;
17333        }"
17334        .unindent(),
17335        cx,
17336    )
17337    .await;
17338
17339    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
17340}
17341
17342#[gpui::test]
17343async fn test_indent_guide_nested(cx: &mut TestAppContext) {
17344    let (buffer_id, mut cx) = setup_indent_guides_editor(
17345        &"
17346        fn main() {
17347            let a = 1;
17348            if a == 3 {
17349                let b = 2;
17350            } else {
17351                let c = 3;
17352            }
17353        }"
17354        .unindent(),
17355        cx,
17356    )
17357    .await;
17358
17359    assert_indent_guides(
17360        0..8,
17361        vec![
17362            indent_guide(buffer_id, 1, 6, 0),
17363            indent_guide(buffer_id, 3, 3, 1),
17364            indent_guide(buffer_id, 5, 5, 1),
17365        ],
17366        None,
17367        &mut cx,
17368    );
17369}
17370
17371#[gpui::test]
17372async fn test_indent_guide_tab(cx: &mut TestAppContext) {
17373    let (buffer_id, mut cx) = setup_indent_guides_editor(
17374        &"
17375        fn main() {
17376            let a = 1;
17377                let b = 2;
17378            let c = 3;
17379        }"
17380        .unindent(),
17381        cx,
17382    )
17383    .await;
17384
17385    assert_indent_guides(
17386        0..5,
17387        vec![
17388            indent_guide(buffer_id, 1, 3, 0),
17389            indent_guide(buffer_id, 2, 2, 1),
17390        ],
17391        None,
17392        &mut cx,
17393    );
17394}
17395
17396#[gpui::test]
17397async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
17398    let (buffer_id, mut cx) = setup_indent_guides_editor(
17399        &"
17400        fn main() {
17401            let a = 1;
17402
17403            let c = 3;
17404        }"
17405        .unindent(),
17406        cx,
17407    )
17408    .await;
17409
17410    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
17411}
17412
17413#[gpui::test]
17414async fn test_indent_guide_complex(cx: &mut TestAppContext) {
17415    let (buffer_id, mut cx) = setup_indent_guides_editor(
17416        &"
17417        fn main() {
17418            let a = 1;
17419
17420            let c = 3;
17421
17422            if a == 3 {
17423                let b = 2;
17424            } else {
17425                let c = 3;
17426            }
17427        }"
17428        .unindent(),
17429        cx,
17430    )
17431    .await;
17432
17433    assert_indent_guides(
17434        0..11,
17435        vec![
17436            indent_guide(buffer_id, 1, 9, 0),
17437            indent_guide(buffer_id, 6, 6, 1),
17438            indent_guide(buffer_id, 8, 8, 1),
17439        ],
17440        None,
17441        &mut cx,
17442    );
17443}
17444
17445#[gpui::test]
17446async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
17447    let (buffer_id, mut cx) = setup_indent_guides_editor(
17448        &"
17449        fn main() {
17450            let a = 1;
17451
17452            let c = 3;
17453
17454            if a == 3 {
17455                let b = 2;
17456            } else {
17457                let c = 3;
17458            }
17459        }"
17460        .unindent(),
17461        cx,
17462    )
17463    .await;
17464
17465    assert_indent_guides(
17466        1..11,
17467        vec![
17468            indent_guide(buffer_id, 1, 9, 0),
17469            indent_guide(buffer_id, 6, 6, 1),
17470            indent_guide(buffer_id, 8, 8, 1),
17471        ],
17472        None,
17473        &mut cx,
17474    );
17475}
17476
17477#[gpui::test]
17478async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
17479    let (buffer_id, mut cx) = setup_indent_guides_editor(
17480        &"
17481        fn main() {
17482            let a = 1;
17483
17484            let c = 3;
17485
17486            if a == 3 {
17487                let b = 2;
17488            } else {
17489                let c = 3;
17490            }
17491        }"
17492        .unindent(),
17493        cx,
17494    )
17495    .await;
17496
17497    assert_indent_guides(
17498        1..10,
17499        vec![
17500            indent_guide(buffer_id, 1, 9, 0),
17501            indent_guide(buffer_id, 6, 6, 1),
17502            indent_guide(buffer_id, 8, 8, 1),
17503        ],
17504        None,
17505        &mut cx,
17506    );
17507}
17508
17509#[gpui::test]
17510async fn test_indent_guide_with_folds(cx: &mut TestAppContext) {
17511    let (buffer_id, mut cx) = setup_indent_guides_editor(
17512        &"
17513        fn main() {
17514            if a {
17515                b(
17516                    c,
17517                    d,
17518                )
17519            } else {
17520                e(
17521                    f
17522                )
17523            }
17524        }"
17525        .unindent(),
17526        cx,
17527    )
17528    .await;
17529
17530    assert_indent_guides(
17531        0..11,
17532        vec![
17533            indent_guide(buffer_id, 1, 10, 0),
17534            indent_guide(buffer_id, 2, 5, 1),
17535            indent_guide(buffer_id, 7, 9, 1),
17536            indent_guide(buffer_id, 3, 4, 2),
17537            indent_guide(buffer_id, 8, 8, 2),
17538        ],
17539        None,
17540        &mut cx,
17541    );
17542
17543    cx.update_editor(|editor, window, cx| {
17544        editor.fold_at(MultiBufferRow(2), window, cx);
17545        assert_eq!(
17546            editor.display_text(cx),
17547            "
17548            fn main() {
17549                if a {
17550                    b(⋯
17551                    )
17552                } else {
17553                    e(
17554                        f
17555                    )
17556                }
17557            }"
17558            .unindent()
17559        );
17560    });
17561
17562    assert_indent_guides(
17563        0..11,
17564        vec![
17565            indent_guide(buffer_id, 1, 10, 0),
17566            indent_guide(buffer_id, 2, 5, 1),
17567            indent_guide(buffer_id, 7, 9, 1),
17568            indent_guide(buffer_id, 8, 8, 2),
17569        ],
17570        None,
17571        &mut cx,
17572    );
17573}
17574
17575#[gpui::test]
17576async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
17577    let (buffer_id, mut cx) = setup_indent_guides_editor(
17578        &"
17579        block1
17580            block2
17581                block3
17582                    block4
17583            block2
17584        block1
17585        block1"
17586            .unindent(),
17587        cx,
17588    )
17589    .await;
17590
17591    assert_indent_guides(
17592        1..10,
17593        vec![
17594            indent_guide(buffer_id, 1, 4, 0),
17595            indent_guide(buffer_id, 2, 3, 1),
17596            indent_guide(buffer_id, 3, 3, 2),
17597        ],
17598        None,
17599        &mut cx,
17600    );
17601}
17602
17603#[gpui::test]
17604async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
17605    let (buffer_id, mut cx) = setup_indent_guides_editor(
17606        &"
17607        block1
17608            block2
17609                block3
17610
17611        block1
17612        block1"
17613            .unindent(),
17614        cx,
17615    )
17616    .await;
17617
17618    assert_indent_guides(
17619        0..6,
17620        vec![
17621            indent_guide(buffer_id, 1, 2, 0),
17622            indent_guide(buffer_id, 2, 2, 1),
17623        ],
17624        None,
17625        &mut cx,
17626    );
17627}
17628
17629#[gpui::test]
17630async fn test_indent_guide_ignored_only_whitespace_lines(cx: &mut TestAppContext) {
17631    let (buffer_id, mut cx) = setup_indent_guides_editor(
17632        &"
17633        function component() {
17634        \treturn (
17635        \t\t\t
17636        \t\t<div>
17637        \t\t\t<abc></abc>
17638        \t\t</div>
17639        \t)
17640        }"
17641        .unindent(),
17642        cx,
17643    )
17644    .await;
17645
17646    assert_indent_guides(
17647        0..8,
17648        vec![
17649            indent_guide(buffer_id, 1, 6, 0),
17650            indent_guide(buffer_id, 2, 5, 1),
17651            indent_guide(buffer_id, 4, 4, 2),
17652        ],
17653        None,
17654        &mut cx,
17655    );
17656}
17657
17658#[gpui::test]
17659async fn test_indent_guide_fallback_to_next_non_entirely_whitespace_line(cx: &mut TestAppContext) {
17660    let (buffer_id, mut cx) = setup_indent_guides_editor(
17661        &"
17662        function component() {
17663        \treturn (
17664        \t
17665        \t\t<div>
17666        \t\t\t<abc></abc>
17667        \t\t</div>
17668        \t)
17669        }"
17670        .unindent(),
17671        cx,
17672    )
17673    .await;
17674
17675    assert_indent_guides(
17676        0..8,
17677        vec![
17678            indent_guide(buffer_id, 1, 6, 0),
17679            indent_guide(buffer_id, 2, 5, 1),
17680            indent_guide(buffer_id, 4, 4, 2),
17681        ],
17682        None,
17683        &mut cx,
17684    );
17685}
17686
17687#[gpui::test]
17688async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
17689    let (buffer_id, mut cx) = setup_indent_guides_editor(
17690        &"
17691        block1
17692
17693
17694
17695            block2
17696        "
17697        .unindent(),
17698        cx,
17699    )
17700    .await;
17701
17702    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
17703}
17704
17705#[gpui::test]
17706async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
17707    let (buffer_id, mut cx) = setup_indent_guides_editor(
17708        &"
17709        def a:
17710        \tb = 3
17711        \tif True:
17712        \t\tc = 4
17713        \t\td = 5
17714        \tprint(b)
17715        "
17716        .unindent(),
17717        cx,
17718    )
17719    .await;
17720
17721    assert_indent_guides(
17722        0..6,
17723        vec![
17724            indent_guide(buffer_id, 1, 5, 0),
17725            indent_guide(buffer_id, 3, 4, 1),
17726        ],
17727        None,
17728        &mut cx,
17729    );
17730}
17731
17732#[gpui::test]
17733async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
17734    let (buffer_id, mut cx) = setup_indent_guides_editor(
17735        &"
17736    fn main() {
17737        let a = 1;
17738    }"
17739        .unindent(),
17740        cx,
17741    )
17742    .await;
17743
17744    cx.update_editor(|editor, window, cx| {
17745        editor.change_selections(None, window, cx, |s| {
17746            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17747        });
17748    });
17749
17750    assert_indent_guides(
17751        0..3,
17752        vec![indent_guide(buffer_id, 1, 1, 0)],
17753        Some(vec![0]),
17754        &mut cx,
17755    );
17756}
17757
17758#[gpui::test]
17759async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
17760    let (buffer_id, mut cx) = setup_indent_guides_editor(
17761        &"
17762    fn main() {
17763        if 1 == 2 {
17764            let a = 1;
17765        }
17766    }"
17767        .unindent(),
17768        cx,
17769    )
17770    .await;
17771
17772    cx.update_editor(|editor, window, cx| {
17773        editor.change_selections(None, window, cx, |s| {
17774            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17775        });
17776    });
17777
17778    assert_indent_guides(
17779        0..4,
17780        vec![
17781            indent_guide(buffer_id, 1, 3, 0),
17782            indent_guide(buffer_id, 2, 2, 1),
17783        ],
17784        Some(vec![1]),
17785        &mut cx,
17786    );
17787
17788    cx.update_editor(|editor, window, cx| {
17789        editor.change_selections(None, window, cx, |s| {
17790            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17791        });
17792    });
17793
17794    assert_indent_guides(
17795        0..4,
17796        vec![
17797            indent_guide(buffer_id, 1, 3, 0),
17798            indent_guide(buffer_id, 2, 2, 1),
17799        ],
17800        Some(vec![1]),
17801        &mut cx,
17802    );
17803
17804    cx.update_editor(|editor, window, cx| {
17805        editor.change_selections(None, window, cx, |s| {
17806            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
17807        });
17808    });
17809
17810    assert_indent_guides(
17811        0..4,
17812        vec![
17813            indent_guide(buffer_id, 1, 3, 0),
17814            indent_guide(buffer_id, 2, 2, 1),
17815        ],
17816        Some(vec![0]),
17817        &mut cx,
17818    );
17819}
17820
17821#[gpui::test]
17822async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
17823    let (buffer_id, mut cx) = setup_indent_guides_editor(
17824        &"
17825    fn main() {
17826        let a = 1;
17827
17828        let b = 2;
17829    }"
17830        .unindent(),
17831        cx,
17832    )
17833    .await;
17834
17835    cx.update_editor(|editor, window, cx| {
17836        editor.change_selections(None, window, cx, |s| {
17837            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17838        });
17839    });
17840
17841    assert_indent_guides(
17842        0..5,
17843        vec![indent_guide(buffer_id, 1, 3, 0)],
17844        Some(vec![0]),
17845        &mut cx,
17846    );
17847}
17848
17849#[gpui::test]
17850async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17851    let (buffer_id, mut cx) = setup_indent_guides_editor(
17852        &"
17853    def m:
17854        a = 1
17855        pass"
17856            .unindent(),
17857        cx,
17858    )
17859    .await;
17860
17861    cx.update_editor(|editor, window, cx| {
17862        editor.change_selections(None, window, cx, |s| {
17863            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17864        });
17865    });
17866
17867    assert_indent_guides(
17868        0..3,
17869        vec![indent_guide(buffer_id, 1, 2, 0)],
17870        Some(vec![0]),
17871        &mut cx,
17872    );
17873}
17874
17875#[gpui::test]
17876async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17877    init_test(cx, |_| {});
17878    let mut cx = EditorTestContext::new(cx).await;
17879    let text = indoc! {
17880        "
17881        impl A {
17882            fn b() {
17883                0;
17884                3;
17885                5;
17886                6;
17887                7;
17888            }
17889        }
17890        "
17891    };
17892    let base_text = indoc! {
17893        "
17894        impl A {
17895            fn b() {
17896                0;
17897                1;
17898                2;
17899                3;
17900                4;
17901            }
17902            fn c() {
17903                5;
17904                6;
17905                7;
17906            }
17907        }
17908        "
17909    };
17910
17911    cx.update_editor(|editor, window, cx| {
17912        editor.set_text(text, window, cx);
17913
17914        editor.buffer().update(cx, |multibuffer, cx| {
17915            let buffer = multibuffer.as_singleton().unwrap();
17916            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17917
17918            multibuffer.set_all_diff_hunks_expanded(cx);
17919            multibuffer.add_diff(diff, cx);
17920
17921            buffer.read(cx).remote_id()
17922        })
17923    });
17924    cx.run_until_parked();
17925
17926    cx.assert_state_with_diff(
17927        indoc! { "
17928          impl A {
17929              fn b() {
17930                  0;
17931        -         1;
17932        -         2;
17933                  3;
17934        -         4;
17935        -     }
17936        -     fn c() {
17937                  5;
17938                  6;
17939                  7;
17940              }
17941          }
17942          ˇ"
17943        }
17944        .to_string(),
17945    );
17946
17947    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17948        editor
17949            .snapshot(window, cx)
17950            .buffer_snapshot
17951            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17952            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17953            .collect::<Vec<_>>()
17954    });
17955    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17956    assert_eq!(
17957        actual_guides,
17958        vec![
17959            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17960            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17961            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17962        ]
17963    );
17964}
17965
17966#[gpui::test]
17967async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17968    init_test(cx, |_| {});
17969    let mut cx = EditorTestContext::new(cx).await;
17970
17971    let diff_base = r#"
17972        a
17973        b
17974        c
17975        "#
17976    .unindent();
17977
17978    cx.set_state(
17979        &r#"
17980        ˇA
17981        b
17982        C
17983        "#
17984        .unindent(),
17985    );
17986    cx.set_head_text(&diff_base);
17987    cx.update_editor(|editor, window, cx| {
17988        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17989    });
17990    executor.run_until_parked();
17991
17992    let both_hunks_expanded = r#"
17993        - a
17994        + ˇA
17995          b
17996        - c
17997        + C
17998        "#
17999    .unindent();
18000
18001    cx.assert_state_with_diff(both_hunks_expanded.clone());
18002
18003    let hunk_ranges = cx.update_editor(|editor, window, cx| {
18004        let snapshot = editor.snapshot(window, cx);
18005        let hunks = editor
18006            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
18007            .collect::<Vec<_>>();
18008        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
18009        let buffer_id = hunks[0].buffer_id;
18010        hunks
18011            .into_iter()
18012            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
18013            .collect::<Vec<_>>()
18014    });
18015    assert_eq!(hunk_ranges.len(), 2);
18016
18017    cx.update_editor(|editor, _, cx| {
18018        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
18019    });
18020    executor.run_until_parked();
18021
18022    let second_hunk_expanded = r#"
18023          ˇA
18024          b
18025        - c
18026        + C
18027        "#
18028    .unindent();
18029
18030    cx.assert_state_with_diff(second_hunk_expanded);
18031
18032    cx.update_editor(|editor, _, cx| {
18033        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
18034    });
18035    executor.run_until_parked();
18036
18037    cx.assert_state_with_diff(both_hunks_expanded.clone());
18038
18039    cx.update_editor(|editor, _, cx| {
18040        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
18041    });
18042    executor.run_until_parked();
18043
18044    let first_hunk_expanded = r#"
18045        - a
18046        + ˇA
18047          b
18048          C
18049        "#
18050    .unindent();
18051
18052    cx.assert_state_with_diff(first_hunk_expanded);
18053
18054    cx.update_editor(|editor, _, cx| {
18055        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
18056    });
18057    executor.run_until_parked();
18058
18059    cx.assert_state_with_diff(both_hunks_expanded);
18060
18061    cx.set_state(
18062        &r#"
18063        ˇA
18064        b
18065        "#
18066        .unindent(),
18067    );
18068    cx.run_until_parked();
18069
18070    // TODO this cursor position seems bad
18071    cx.assert_state_with_diff(
18072        r#"
18073        - ˇa
18074        + A
18075          b
18076        "#
18077        .unindent(),
18078    );
18079
18080    cx.update_editor(|editor, window, cx| {
18081        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
18082    });
18083
18084    cx.assert_state_with_diff(
18085        r#"
18086            - ˇa
18087            + A
18088              b
18089            - c
18090            "#
18091        .unindent(),
18092    );
18093
18094    let hunk_ranges = cx.update_editor(|editor, window, cx| {
18095        let snapshot = editor.snapshot(window, cx);
18096        let hunks = editor
18097            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
18098            .collect::<Vec<_>>();
18099        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
18100        let buffer_id = hunks[0].buffer_id;
18101        hunks
18102            .into_iter()
18103            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
18104            .collect::<Vec<_>>()
18105    });
18106    assert_eq!(hunk_ranges.len(), 2);
18107
18108    cx.update_editor(|editor, _, cx| {
18109        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
18110    });
18111    executor.run_until_parked();
18112
18113    cx.assert_state_with_diff(
18114        r#"
18115        - ˇa
18116        + A
18117          b
18118        "#
18119        .unindent(),
18120    );
18121}
18122
18123#[gpui::test]
18124async fn test_toggle_deletion_hunk_at_start_of_file(
18125    executor: BackgroundExecutor,
18126    cx: &mut TestAppContext,
18127) {
18128    init_test(cx, |_| {});
18129    let mut cx = EditorTestContext::new(cx).await;
18130
18131    let diff_base = r#"
18132        a
18133        b
18134        c
18135        "#
18136    .unindent();
18137
18138    cx.set_state(
18139        &r#"
18140        ˇb
18141        c
18142        "#
18143        .unindent(),
18144    );
18145    cx.set_head_text(&diff_base);
18146    cx.update_editor(|editor, window, cx| {
18147        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
18148    });
18149    executor.run_until_parked();
18150
18151    let hunk_expanded = r#"
18152        - a
18153          ˇb
18154          c
18155        "#
18156    .unindent();
18157
18158    cx.assert_state_with_diff(hunk_expanded.clone());
18159
18160    let hunk_ranges = cx.update_editor(|editor, window, cx| {
18161        let snapshot = editor.snapshot(window, cx);
18162        let hunks = editor
18163            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
18164            .collect::<Vec<_>>();
18165        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
18166        let buffer_id = hunks[0].buffer_id;
18167        hunks
18168            .into_iter()
18169            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
18170            .collect::<Vec<_>>()
18171    });
18172    assert_eq!(hunk_ranges.len(), 1);
18173
18174    cx.update_editor(|editor, _, cx| {
18175        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
18176    });
18177    executor.run_until_parked();
18178
18179    let hunk_collapsed = r#"
18180          ˇb
18181          c
18182        "#
18183    .unindent();
18184
18185    cx.assert_state_with_diff(hunk_collapsed);
18186
18187    cx.update_editor(|editor, _, cx| {
18188        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
18189    });
18190    executor.run_until_parked();
18191
18192    cx.assert_state_with_diff(hunk_expanded.clone());
18193}
18194
18195#[gpui::test]
18196async fn test_display_diff_hunks(cx: &mut TestAppContext) {
18197    init_test(cx, |_| {});
18198
18199    let fs = FakeFs::new(cx.executor());
18200    fs.insert_tree(
18201        path!("/test"),
18202        json!({
18203            ".git": {},
18204            "file-1": "ONE\n",
18205            "file-2": "TWO\n",
18206            "file-3": "THREE\n",
18207        }),
18208    )
18209    .await;
18210
18211    fs.set_head_for_repo(
18212        path!("/test/.git").as_ref(),
18213        &[
18214            ("file-1".into(), "one\n".into()),
18215            ("file-2".into(), "two\n".into()),
18216            ("file-3".into(), "three\n".into()),
18217        ],
18218        "deadbeef",
18219    );
18220
18221    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
18222    let mut buffers = vec![];
18223    for i in 1..=3 {
18224        let buffer = project
18225            .update(cx, |project, cx| {
18226                let path = format!(path!("/test/file-{}"), i);
18227                project.open_local_buffer(path, cx)
18228            })
18229            .await
18230            .unwrap();
18231        buffers.push(buffer);
18232    }
18233
18234    let multibuffer = cx.new(|cx| {
18235        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
18236        multibuffer.set_all_diff_hunks_expanded(cx);
18237        for buffer in &buffers {
18238            let snapshot = buffer.read(cx).snapshot();
18239            multibuffer.set_excerpts_for_path(
18240                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
18241                buffer.clone(),
18242                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
18243                DEFAULT_MULTIBUFFER_CONTEXT,
18244                cx,
18245            );
18246        }
18247        multibuffer
18248    });
18249
18250    let editor = cx.add_window(|window, cx| {
18251        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
18252    });
18253    cx.run_until_parked();
18254
18255    let snapshot = editor
18256        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
18257        .unwrap();
18258    let hunks = snapshot
18259        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
18260        .map(|hunk| match hunk {
18261            DisplayDiffHunk::Unfolded {
18262                display_row_range, ..
18263            } => display_row_range,
18264            DisplayDiffHunk::Folded { .. } => unreachable!(),
18265        })
18266        .collect::<Vec<_>>();
18267    assert_eq!(
18268        hunks,
18269        [
18270            DisplayRow(2)..DisplayRow(4),
18271            DisplayRow(7)..DisplayRow(9),
18272            DisplayRow(12)..DisplayRow(14),
18273        ]
18274    );
18275}
18276
18277#[gpui::test]
18278async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
18279    init_test(cx, |_| {});
18280
18281    let mut cx = EditorTestContext::new(cx).await;
18282    cx.set_head_text(indoc! { "
18283        one
18284        two
18285        three
18286        four
18287        five
18288        "
18289    });
18290    cx.set_index_text(indoc! { "
18291        one
18292        two
18293        three
18294        four
18295        five
18296        "
18297    });
18298    cx.set_state(indoc! {"
18299        one
18300        TWO
18301        ˇTHREE
18302        FOUR
18303        five
18304    "});
18305    cx.run_until_parked();
18306    cx.update_editor(|editor, window, cx| {
18307        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
18308    });
18309    cx.run_until_parked();
18310    cx.assert_index_text(Some(indoc! {"
18311        one
18312        TWO
18313        THREE
18314        FOUR
18315        five
18316    "}));
18317    cx.set_state(indoc! { "
18318        one
18319        TWO
18320        ˇTHREE-HUNDRED
18321        FOUR
18322        five
18323    "});
18324    cx.run_until_parked();
18325    cx.update_editor(|editor, window, cx| {
18326        let snapshot = editor.snapshot(window, cx);
18327        let hunks = editor
18328            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
18329            .collect::<Vec<_>>();
18330        assert_eq!(hunks.len(), 1);
18331        assert_eq!(
18332            hunks[0].status(),
18333            DiffHunkStatus {
18334                kind: DiffHunkStatusKind::Modified,
18335                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
18336            }
18337        );
18338
18339        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
18340    });
18341    cx.run_until_parked();
18342    cx.assert_index_text(Some(indoc! {"
18343        one
18344        TWO
18345        THREE-HUNDRED
18346        FOUR
18347        five
18348    "}));
18349}
18350
18351#[gpui::test]
18352fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
18353    init_test(cx, |_| {});
18354
18355    let editor = cx.add_window(|window, cx| {
18356        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
18357        build_editor(buffer, window, cx)
18358    });
18359
18360    let render_args = Arc::new(Mutex::new(None));
18361    let snapshot = editor
18362        .update(cx, |editor, window, cx| {
18363            let snapshot = editor.buffer().read(cx).snapshot(cx);
18364            let range =
18365                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
18366
18367            struct RenderArgs {
18368                row: MultiBufferRow,
18369                folded: bool,
18370                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
18371            }
18372
18373            let crease = Crease::inline(
18374                range,
18375                FoldPlaceholder::test(),
18376                {
18377                    let toggle_callback = render_args.clone();
18378                    move |row, folded, callback, _window, _cx| {
18379                        *toggle_callback.lock() = Some(RenderArgs {
18380                            row,
18381                            folded,
18382                            callback,
18383                        });
18384                        div()
18385                    }
18386                },
18387                |_row, _folded, _window, _cx| div(),
18388            );
18389
18390            editor.insert_creases(Some(crease), cx);
18391            let snapshot = editor.snapshot(window, cx);
18392            let _div = snapshot.render_crease_toggle(
18393                MultiBufferRow(1),
18394                false,
18395                cx.entity().clone(),
18396                window,
18397                cx,
18398            );
18399            snapshot
18400        })
18401        .unwrap();
18402
18403    let render_args = render_args.lock().take().unwrap();
18404    assert_eq!(render_args.row, MultiBufferRow(1));
18405    assert!(!render_args.folded);
18406    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
18407
18408    cx.update_window(*editor, |_, window, cx| {
18409        (render_args.callback)(true, window, cx)
18410    })
18411    .unwrap();
18412    let snapshot = editor
18413        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
18414        .unwrap();
18415    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
18416
18417    cx.update_window(*editor, |_, window, cx| {
18418        (render_args.callback)(false, window, cx)
18419    })
18420    .unwrap();
18421    let snapshot = editor
18422        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
18423        .unwrap();
18424    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
18425}
18426
18427#[gpui::test]
18428async fn test_input_text(cx: &mut TestAppContext) {
18429    init_test(cx, |_| {});
18430    let mut cx = EditorTestContext::new(cx).await;
18431
18432    cx.set_state(
18433        &r#"ˇone
18434        two
18435
18436        three
18437        fourˇ
18438        five
18439
18440        siˇx"#
18441            .unindent(),
18442    );
18443
18444    cx.dispatch_action(HandleInput(String::new()));
18445    cx.assert_editor_state(
18446        &r#"ˇone
18447        two
18448
18449        three
18450        fourˇ
18451        five
18452
18453        siˇx"#
18454            .unindent(),
18455    );
18456
18457    cx.dispatch_action(HandleInput("AAAA".to_string()));
18458    cx.assert_editor_state(
18459        &r#"AAAAˇone
18460        two
18461
18462        three
18463        fourAAAAˇ
18464        five
18465
18466        siAAAAˇx"#
18467            .unindent(),
18468    );
18469}
18470
18471#[gpui::test]
18472async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
18473    init_test(cx, |_| {});
18474
18475    let mut cx = EditorTestContext::new(cx).await;
18476    cx.set_state(
18477        r#"let foo = 1;
18478let foo = 2;
18479let foo = 3;
18480let fooˇ = 4;
18481let foo = 5;
18482let foo = 6;
18483let foo = 7;
18484let foo = 8;
18485let foo = 9;
18486let foo = 10;
18487let foo = 11;
18488let foo = 12;
18489let foo = 13;
18490let foo = 14;
18491let foo = 15;"#,
18492    );
18493
18494    cx.update_editor(|e, window, cx| {
18495        assert_eq!(
18496            e.next_scroll_position,
18497            NextScrollCursorCenterTopBottom::Center,
18498            "Default next scroll direction is center",
18499        );
18500
18501        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18502        assert_eq!(
18503            e.next_scroll_position,
18504            NextScrollCursorCenterTopBottom::Top,
18505            "After center, next scroll direction should be top",
18506        );
18507
18508        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18509        assert_eq!(
18510            e.next_scroll_position,
18511            NextScrollCursorCenterTopBottom::Bottom,
18512            "After top, next scroll direction should be bottom",
18513        );
18514
18515        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18516        assert_eq!(
18517            e.next_scroll_position,
18518            NextScrollCursorCenterTopBottom::Center,
18519            "After bottom, scrolling should start over",
18520        );
18521
18522        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18523        assert_eq!(
18524            e.next_scroll_position,
18525            NextScrollCursorCenterTopBottom::Top,
18526            "Scrolling continues if retriggered fast enough"
18527        );
18528    });
18529
18530    cx.executor()
18531        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
18532    cx.executor().run_until_parked();
18533    cx.update_editor(|e, _, _| {
18534        assert_eq!(
18535            e.next_scroll_position,
18536            NextScrollCursorCenterTopBottom::Center,
18537            "If scrolling is not triggered fast enough, it should reset"
18538        );
18539    });
18540}
18541
18542#[gpui::test]
18543async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
18544    init_test(cx, |_| {});
18545    let mut cx = EditorLspTestContext::new_rust(
18546        lsp::ServerCapabilities {
18547            definition_provider: Some(lsp::OneOf::Left(true)),
18548            references_provider: Some(lsp::OneOf::Left(true)),
18549            ..lsp::ServerCapabilities::default()
18550        },
18551        cx,
18552    )
18553    .await;
18554
18555    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
18556        let go_to_definition = cx
18557            .lsp
18558            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
18559                move |params, _| async move {
18560                    if empty_go_to_definition {
18561                        Ok(None)
18562                    } else {
18563                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
18564                            uri: params.text_document_position_params.text_document.uri,
18565                            range: lsp::Range::new(
18566                                lsp::Position::new(4, 3),
18567                                lsp::Position::new(4, 6),
18568                            ),
18569                        })))
18570                    }
18571                },
18572            );
18573        let references = cx
18574            .lsp
18575            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
18576                Ok(Some(vec![lsp::Location {
18577                    uri: params.text_document_position.text_document.uri,
18578                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
18579                }]))
18580            });
18581        (go_to_definition, references)
18582    };
18583
18584    cx.set_state(
18585        &r#"fn one() {
18586            let mut a = ˇtwo();
18587        }
18588
18589        fn two() {}"#
18590            .unindent(),
18591    );
18592    set_up_lsp_handlers(false, &mut cx);
18593    let navigated = cx
18594        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18595        .await
18596        .expect("Failed to navigate to definition");
18597    assert_eq!(
18598        navigated,
18599        Navigated::Yes,
18600        "Should have navigated to definition from the GetDefinition response"
18601    );
18602    cx.assert_editor_state(
18603        &r#"fn one() {
18604            let mut a = two();
18605        }
18606
18607        fn «twoˇ»() {}"#
18608            .unindent(),
18609    );
18610
18611    let editors = cx.update_workspace(|workspace, _, cx| {
18612        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18613    });
18614    cx.update_editor(|_, _, test_editor_cx| {
18615        assert_eq!(
18616            editors.len(),
18617            1,
18618            "Initially, only one, test, editor should be open in the workspace"
18619        );
18620        assert_eq!(
18621            test_editor_cx.entity(),
18622            editors.last().expect("Asserted len is 1").clone()
18623        );
18624    });
18625
18626    set_up_lsp_handlers(true, &mut cx);
18627    let navigated = cx
18628        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18629        .await
18630        .expect("Failed to navigate to lookup references");
18631    assert_eq!(
18632        navigated,
18633        Navigated::Yes,
18634        "Should have navigated to references as a fallback after empty GoToDefinition response"
18635    );
18636    // We should not change the selections in the existing file,
18637    // if opening another milti buffer with the references
18638    cx.assert_editor_state(
18639        &r#"fn one() {
18640            let mut a = two();
18641        }
18642
18643        fn «twoˇ»() {}"#
18644            .unindent(),
18645    );
18646    let editors = cx.update_workspace(|workspace, _, cx| {
18647        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18648    });
18649    cx.update_editor(|_, _, test_editor_cx| {
18650        assert_eq!(
18651            editors.len(),
18652            2,
18653            "After falling back to references search, we open a new editor with the results"
18654        );
18655        let references_fallback_text = editors
18656            .into_iter()
18657            .find(|new_editor| *new_editor != test_editor_cx.entity())
18658            .expect("Should have one non-test editor now")
18659            .read(test_editor_cx)
18660            .text(test_editor_cx);
18661        assert_eq!(
18662            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
18663            "Should use the range from the references response and not the GoToDefinition one"
18664        );
18665    });
18666}
18667
18668#[gpui::test]
18669async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
18670    init_test(cx, |_| {});
18671    cx.update(|cx| {
18672        let mut editor_settings = EditorSettings::get_global(cx).clone();
18673        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
18674        EditorSettings::override_global(editor_settings, cx);
18675    });
18676    let mut cx = EditorLspTestContext::new_rust(
18677        lsp::ServerCapabilities {
18678            definition_provider: Some(lsp::OneOf::Left(true)),
18679            references_provider: Some(lsp::OneOf::Left(true)),
18680            ..lsp::ServerCapabilities::default()
18681        },
18682        cx,
18683    )
18684    .await;
18685    let original_state = r#"fn one() {
18686        let mut a = ˇtwo();
18687    }
18688
18689    fn two() {}"#
18690        .unindent();
18691    cx.set_state(&original_state);
18692
18693    let mut go_to_definition = cx
18694        .lsp
18695        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
18696            move |_, _| async move { Ok(None) },
18697        );
18698    let _references = cx
18699        .lsp
18700        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
18701            panic!("Should not call for references with no go to definition fallback")
18702        });
18703
18704    let navigated = cx
18705        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18706        .await
18707        .expect("Failed to navigate to lookup references");
18708    go_to_definition
18709        .next()
18710        .await
18711        .expect("Should have called the go_to_definition handler");
18712
18713    assert_eq!(
18714        navigated,
18715        Navigated::No,
18716        "Should have navigated to references as a fallback after empty GoToDefinition response"
18717    );
18718    cx.assert_editor_state(&original_state);
18719    let editors = cx.update_workspace(|workspace, _, cx| {
18720        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18721    });
18722    cx.update_editor(|_, _, _| {
18723        assert_eq!(
18724            editors.len(),
18725            1,
18726            "After unsuccessful fallback, no other editor should have been opened"
18727        );
18728    });
18729}
18730
18731#[gpui::test]
18732async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
18733    init_test(cx, |_| {});
18734
18735    let language = Arc::new(Language::new(
18736        LanguageConfig::default(),
18737        Some(tree_sitter_rust::LANGUAGE.into()),
18738    ));
18739
18740    let text = r#"
18741        #[cfg(test)]
18742        mod tests() {
18743            #[test]
18744            fn runnable_1() {
18745                let a = 1;
18746            }
18747
18748            #[test]
18749            fn runnable_2() {
18750                let a = 1;
18751                let b = 2;
18752            }
18753        }
18754    "#
18755    .unindent();
18756
18757    let fs = FakeFs::new(cx.executor());
18758    fs.insert_file("/file.rs", Default::default()).await;
18759
18760    let project = Project::test(fs, ["/a".as_ref()], cx).await;
18761    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18762    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18763    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
18764    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
18765
18766    let editor = cx.new_window_entity(|window, cx| {
18767        Editor::new(
18768            EditorMode::full(),
18769            multi_buffer,
18770            Some(project.clone()),
18771            window,
18772            cx,
18773        )
18774    });
18775
18776    editor.update_in(cx, |editor, window, cx| {
18777        let snapshot = editor.buffer().read(cx).snapshot(cx);
18778        editor.tasks.insert(
18779            (buffer.read(cx).remote_id(), 3),
18780            RunnableTasks {
18781                templates: vec![],
18782                offset: snapshot.anchor_before(43),
18783                column: 0,
18784                extra_variables: HashMap::default(),
18785                context_range: BufferOffset(43)..BufferOffset(85),
18786            },
18787        );
18788        editor.tasks.insert(
18789            (buffer.read(cx).remote_id(), 8),
18790            RunnableTasks {
18791                templates: vec![],
18792                offset: snapshot.anchor_before(86),
18793                column: 0,
18794                extra_variables: HashMap::default(),
18795                context_range: BufferOffset(86)..BufferOffset(191),
18796            },
18797        );
18798
18799        // Test finding task when cursor is inside function body
18800        editor.change_selections(None, window, cx, |s| {
18801            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
18802        });
18803        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18804        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
18805
18806        // Test finding task when cursor is on function name
18807        editor.change_selections(None, window, cx, |s| {
18808            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
18809        });
18810        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18811        assert_eq!(row, 8, "Should find task when cursor is on function name");
18812    });
18813}
18814
18815#[gpui::test]
18816async fn test_folding_buffers(cx: &mut TestAppContext) {
18817    init_test(cx, |_| {});
18818
18819    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18820    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
18821    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
18822
18823    let fs = FakeFs::new(cx.executor());
18824    fs.insert_tree(
18825        path!("/a"),
18826        json!({
18827            "first.rs": sample_text_1,
18828            "second.rs": sample_text_2,
18829            "third.rs": sample_text_3,
18830        }),
18831    )
18832    .await;
18833    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18834    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18835    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18836    let worktree = project.update(cx, |project, cx| {
18837        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18838        assert_eq!(worktrees.len(), 1);
18839        worktrees.pop().unwrap()
18840    });
18841    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18842
18843    let buffer_1 = project
18844        .update(cx, |project, cx| {
18845            project.open_buffer((worktree_id, "first.rs"), cx)
18846        })
18847        .await
18848        .unwrap();
18849    let buffer_2 = project
18850        .update(cx, |project, cx| {
18851            project.open_buffer((worktree_id, "second.rs"), cx)
18852        })
18853        .await
18854        .unwrap();
18855    let buffer_3 = project
18856        .update(cx, |project, cx| {
18857            project.open_buffer((worktree_id, "third.rs"), cx)
18858        })
18859        .await
18860        .unwrap();
18861
18862    let multi_buffer = cx.new(|cx| {
18863        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18864        multi_buffer.push_excerpts(
18865            buffer_1.clone(),
18866            [
18867                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18868                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18869                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18870            ],
18871            cx,
18872        );
18873        multi_buffer.push_excerpts(
18874            buffer_2.clone(),
18875            [
18876                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18877                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18878                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18879            ],
18880            cx,
18881        );
18882        multi_buffer.push_excerpts(
18883            buffer_3.clone(),
18884            [
18885                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18886                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18887                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18888            ],
18889            cx,
18890        );
18891        multi_buffer
18892    });
18893    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18894        Editor::new(
18895            EditorMode::full(),
18896            multi_buffer.clone(),
18897            Some(project.clone()),
18898            window,
18899            cx,
18900        )
18901    });
18902
18903    assert_eq!(
18904        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18905        "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18906    );
18907
18908    multi_buffer_editor.update(cx, |editor, cx| {
18909        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18910    });
18911    assert_eq!(
18912        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18913        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18914        "After folding the first buffer, its text should not be displayed"
18915    );
18916
18917    multi_buffer_editor.update(cx, |editor, cx| {
18918        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18919    });
18920    assert_eq!(
18921        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18922        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18923        "After folding the second buffer, its text should not be displayed"
18924    );
18925
18926    multi_buffer_editor.update(cx, |editor, cx| {
18927        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18928    });
18929    assert_eq!(
18930        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18931        "\n\n\n\n\n",
18932        "After folding the third buffer, its text should not be displayed"
18933    );
18934
18935    // Emulate selection inside the fold logic, that should work
18936    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18937        editor
18938            .snapshot(window, cx)
18939            .next_line_boundary(Point::new(0, 4));
18940    });
18941
18942    multi_buffer_editor.update(cx, |editor, cx| {
18943        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18944    });
18945    assert_eq!(
18946        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18947        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18948        "After unfolding the second buffer, its text should be displayed"
18949    );
18950
18951    // Typing inside of buffer 1 causes that buffer to be unfolded.
18952    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18953        assert_eq!(
18954            multi_buffer
18955                .read(cx)
18956                .snapshot(cx)
18957                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18958                .collect::<String>(),
18959            "bbbb"
18960        );
18961        editor.change_selections(None, window, cx, |selections| {
18962            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18963        });
18964        editor.handle_input("B", window, cx);
18965    });
18966
18967    assert_eq!(
18968        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18969        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18970        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18971    );
18972
18973    multi_buffer_editor.update(cx, |editor, cx| {
18974        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18975    });
18976    assert_eq!(
18977        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18978        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18979        "After unfolding the all buffers, all original text should be displayed"
18980    );
18981}
18982
18983#[gpui::test]
18984async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18985    init_test(cx, |_| {});
18986
18987    let sample_text_1 = "1111\n2222\n3333".to_string();
18988    let sample_text_2 = "4444\n5555\n6666".to_string();
18989    let sample_text_3 = "7777\n8888\n9999".to_string();
18990
18991    let fs = FakeFs::new(cx.executor());
18992    fs.insert_tree(
18993        path!("/a"),
18994        json!({
18995            "first.rs": sample_text_1,
18996            "second.rs": sample_text_2,
18997            "third.rs": sample_text_3,
18998        }),
18999    )
19000    .await;
19001    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19002    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19003    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19004    let worktree = project.update(cx, |project, cx| {
19005        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
19006        assert_eq!(worktrees.len(), 1);
19007        worktrees.pop().unwrap()
19008    });
19009    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
19010
19011    let buffer_1 = project
19012        .update(cx, |project, cx| {
19013            project.open_buffer((worktree_id, "first.rs"), cx)
19014        })
19015        .await
19016        .unwrap();
19017    let buffer_2 = project
19018        .update(cx, |project, cx| {
19019            project.open_buffer((worktree_id, "second.rs"), cx)
19020        })
19021        .await
19022        .unwrap();
19023    let buffer_3 = project
19024        .update(cx, |project, cx| {
19025            project.open_buffer((worktree_id, "third.rs"), cx)
19026        })
19027        .await
19028        .unwrap();
19029
19030    let multi_buffer = cx.new(|cx| {
19031        let mut multi_buffer = MultiBuffer::new(ReadWrite);
19032        multi_buffer.push_excerpts(
19033            buffer_1.clone(),
19034            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
19035            cx,
19036        );
19037        multi_buffer.push_excerpts(
19038            buffer_2.clone(),
19039            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
19040            cx,
19041        );
19042        multi_buffer.push_excerpts(
19043            buffer_3.clone(),
19044            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
19045            cx,
19046        );
19047        multi_buffer
19048    });
19049
19050    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
19051        Editor::new(
19052            EditorMode::full(),
19053            multi_buffer,
19054            Some(project.clone()),
19055            window,
19056            cx,
19057        )
19058    });
19059
19060    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
19061    assert_eq!(
19062        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19063        full_text,
19064    );
19065
19066    multi_buffer_editor.update(cx, |editor, cx| {
19067        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
19068    });
19069    assert_eq!(
19070        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19071        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
19072        "After folding the first buffer, its text should not be displayed"
19073    );
19074
19075    multi_buffer_editor.update(cx, |editor, cx| {
19076        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
19077    });
19078
19079    assert_eq!(
19080        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19081        "\n\n\n\n\n\n7777\n8888\n9999",
19082        "After folding the second buffer, its text should not be displayed"
19083    );
19084
19085    multi_buffer_editor.update(cx, |editor, cx| {
19086        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
19087    });
19088    assert_eq!(
19089        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19090        "\n\n\n\n\n",
19091        "After folding the third buffer, its text should not be displayed"
19092    );
19093
19094    multi_buffer_editor.update(cx, |editor, cx| {
19095        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
19096    });
19097    assert_eq!(
19098        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19099        "\n\n\n\n4444\n5555\n6666\n\n",
19100        "After unfolding the second buffer, its text should be displayed"
19101    );
19102
19103    multi_buffer_editor.update(cx, |editor, cx| {
19104        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
19105    });
19106    assert_eq!(
19107        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19108        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
19109        "After unfolding the first buffer, its text should be displayed"
19110    );
19111
19112    multi_buffer_editor.update(cx, |editor, cx| {
19113        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
19114    });
19115    assert_eq!(
19116        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19117        full_text,
19118        "After unfolding all buffers, all original text should be displayed"
19119    );
19120}
19121
19122#[gpui::test]
19123async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
19124    init_test(cx, |_| {});
19125
19126    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
19127
19128    let fs = FakeFs::new(cx.executor());
19129    fs.insert_tree(
19130        path!("/a"),
19131        json!({
19132            "main.rs": sample_text,
19133        }),
19134    )
19135    .await;
19136    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19137    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19138    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19139    let worktree = project.update(cx, |project, cx| {
19140        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
19141        assert_eq!(worktrees.len(), 1);
19142        worktrees.pop().unwrap()
19143    });
19144    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
19145
19146    let buffer_1 = project
19147        .update(cx, |project, cx| {
19148            project.open_buffer((worktree_id, "main.rs"), cx)
19149        })
19150        .await
19151        .unwrap();
19152
19153    let multi_buffer = cx.new(|cx| {
19154        let mut multi_buffer = MultiBuffer::new(ReadWrite);
19155        multi_buffer.push_excerpts(
19156            buffer_1.clone(),
19157            [ExcerptRange::new(
19158                Point::new(0, 0)
19159                    ..Point::new(
19160                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
19161                        0,
19162                    ),
19163            )],
19164            cx,
19165        );
19166        multi_buffer
19167    });
19168    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
19169        Editor::new(
19170            EditorMode::full(),
19171            multi_buffer,
19172            Some(project.clone()),
19173            window,
19174            cx,
19175        )
19176    });
19177
19178    let selection_range = Point::new(1, 0)..Point::new(2, 0);
19179    multi_buffer_editor.update_in(cx, |editor, window, cx| {
19180        enum TestHighlight {}
19181        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
19182        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
19183        editor.highlight_text::<TestHighlight>(
19184            vec![highlight_range.clone()],
19185            HighlightStyle::color(Hsla::green()),
19186            cx,
19187        );
19188        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
19189    });
19190
19191    let full_text = format!("\n\n{sample_text}");
19192    assert_eq!(
19193        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
19194        full_text,
19195    );
19196}
19197
19198#[gpui::test]
19199async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
19200    init_test(cx, |_| {});
19201    cx.update(|cx| {
19202        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
19203            "keymaps/default-linux.json",
19204            cx,
19205        )
19206        .unwrap();
19207        cx.bind_keys(default_key_bindings);
19208    });
19209
19210    let (editor, cx) = cx.add_window_view(|window, cx| {
19211        let multi_buffer = MultiBuffer::build_multi(
19212            [
19213                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
19214                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
19215                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
19216                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
19217            ],
19218            cx,
19219        );
19220        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
19221
19222        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
19223        // fold all but the second buffer, so that we test navigating between two
19224        // adjacent folded buffers, as well as folded buffers at the start and
19225        // end the multibuffer
19226        editor.fold_buffer(buffer_ids[0], cx);
19227        editor.fold_buffer(buffer_ids[2], cx);
19228        editor.fold_buffer(buffer_ids[3], cx);
19229
19230        editor
19231    });
19232    cx.simulate_resize(size(px(1000.), px(1000.)));
19233
19234    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
19235    cx.assert_excerpts_with_selections(indoc! {"
19236        [EXCERPT]
19237        ˇ[FOLDED]
19238        [EXCERPT]
19239        a1
19240        b1
19241        [EXCERPT]
19242        [FOLDED]
19243        [EXCERPT]
19244        [FOLDED]
19245        "
19246    });
19247    cx.simulate_keystroke("down");
19248    cx.assert_excerpts_with_selections(indoc! {"
19249        [EXCERPT]
19250        [FOLDED]
19251        [EXCERPT]
19252        ˇa1
19253        b1
19254        [EXCERPT]
19255        [FOLDED]
19256        [EXCERPT]
19257        [FOLDED]
19258        "
19259    });
19260    cx.simulate_keystroke("down");
19261    cx.assert_excerpts_with_selections(indoc! {"
19262        [EXCERPT]
19263        [FOLDED]
19264        [EXCERPT]
19265        a1
19266        ˇb1
19267        [EXCERPT]
19268        [FOLDED]
19269        [EXCERPT]
19270        [FOLDED]
19271        "
19272    });
19273    cx.simulate_keystroke("down");
19274    cx.assert_excerpts_with_selections(indoc! {"
19275        [EXCERPT]
19276        [FOLDED]
19277        [EXCERPT]
19278        a1
19279        b1
19280        ˇ[EXCERPT]
19281        [FOLDED]
19282        [EXCERPT]
19283        [FOLDED]
19284        "
19285    });
19286    cx.simulate_keystroke("down");
19287    cx.assert_excerpts_with_selections(indoc! {"
19288        [EXCERPT]
19289        [FOLDED]
19290        [EXCERPT]
19291        a1
19292        b1
19293        [EXCERPT]
19294        ˇ[FOLDED]
19295        [EXCERPT]
19296        [FOLDED]
19297        "
19298    });
19299    for _ in 0..5 {
19300        cx.simulate_keystroke("down");
19301        cx.assert_excerpts_with_selections(indoc! {"
19302            [EXCERPT]
19303            [FOLDED]
19304            [EXCERPT]
19305            a1
19306            b1
19307            [EXCERPT]
19308            [FOLDED]
19309            [EXCERPT]
19310            ˇ[FOLDED]
19311            "
19312        });
19313    }
19314
19315    cx.simulate_keystroke("up");
19316    cx.assert_excerpts_with_selections(indoc! {"
19317        [EXCERPT]
19318        [FOLDED]
19319        [EXCERPT]
19320        a1
19321        b1
19322        [EXCERPT]
19323        ˇ[FOLDED]
19324        [EXCERPT]
19325        [FOLDED]
19326        "
19327    });
19328    cx.simulate_keystroke("up");
19329    cx.assert_excerpts_with_selections(indoc! {"
19330        [EXCERPT]
19331        [FOLDED]
19332        [EXCERPT]
19333        a1
19334        b1
19335        ˇ[EXCERPT]
19336        [FOLDED]
19337        [EXCERPT]
19338        [FOLDED]
19339        "
19340    });
19341    cx.simulate_keystroke("up");
19342    cx.assert_excerpts_with_selections(indoc! {"
19343        [EXCERPT]
19344        [FOLDED]
19345        [EXCERPT]
19346        a1
19347        ˇb1
19348        [EXCERPT]
19349        [FOLDED]
19350        [EXCERPT]
19351        [FOLDED]
19352        "
19353    });
19354    cx.simulate_keystroke("up");
19355    cx.assert_excerpts_with_selections(indoc! {"
19356        [EXCERPT]
19357        [FOLDED]
19358        [EXCERPT]
19359        ˇa1
19360        b1
19361        [EXCERPT]
19362        [FOLDED]
19363        [EXCERPT]
19364        [FOLDED]
19365        "
19366    });
19367    for _ in 0..5 {
19368        cx.simulate_keystroke("up");
19369        cx.assert_excerpts_with_selections(indoc! {"
19370            [EXCERPT]
19371            ˇ[FOLDED]
19372            [EXCERPT]
19373            a1
19374            b1
19375            [EXCERPT]
19376            [FOLDED]
19377            [EXCERPT]
19378            [FOLDED]
19379            "
19380        });
19381    }
19382}
19383
19384#[gpui::test]
19385async fn test_inline_completion_text(cx: &mut TestAppContext) {
19386    init_test(cx, |_| {});
19387
19388    // Simple insertion
19389    assert_highlighted_edits(
19390        "Hello, world!",
19391        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
19392        true,
19393        cx,
19394        |highlighted_edits, cx| {
19395            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
19396            assert_eq!(highlighted_edits.highlights.len(), 1);
19397            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
19398            assert_eq!(
19399                highlighted_edits.highlights[0].1.background_color,
19400                Some(cx.theme().status().created_background)
19401            );
19402        },
19403    )
19404    .await;
19405
19406    // Replacement
19407    assert_highlighted_edits(
19408        "This is a test.",
19409        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
19410        false,
19411        cx,
19412        |highlighted_edits, cx| {
19413            assert_eq!(highlighted_edits.text, "That is a test.");
19414            assert_eq!(highlighted_edits.highlights.len(), 1);
19415            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
19416            assert_eq!(
19417                highlighted_edits.highlights[0].1.background_color,
19418                Some(cx.theme().status().created_background)
19419            );
19420        },
19421    )
19422    .await;
19423
19424    // Multiple edits
19425    assert_highlighted_edits(
19426        "Hello, world!",
19427        vec![
19428            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
19429            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
19430        ],
19431        false,
19432        cx,
19433        |highlighted_edits, cx| {
19434            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
19435            assert_eq!(highlighted_edits.highlights.len(), 2);
19436            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
19437            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
19438            assert_eq!(
19439                highlighted_edits.highlights[0].1.background_color,
19440                Some(cx.theme().status().created_background)
19441            );
19442            assert_eq!(
19443                highlighted_edits.highlights[1].1.background_color,
19444                Some(cx.theme().status().created_background)
19445            );
19446        },
19447    )
19448    .await;
19449
19450    // Multiple lines with edits
19451    assert_highlighted_edits(
19452        "First line\nSecond line\nThird line\nFourth line",
19453        vec![
19454            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
19455            (
19456                Point::new(2, 0)..Point::new(2, 10),
19457                "New third line".to_string(),
19458            ),
19459            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
19460        ],
19461        false,
19462        cx,
19463        |highlighted_edits, cx| {
19464            assert_eq!(
19465                highlighted_edits.text,
19466                "Second modified\nNew third line\nFourth updated line"
19467            );
19468            assert_eq!(highlighted_edits.highlights.len(), 3);
19469            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
19470            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
19471            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
19472            for highlight in &highlighted_edits.highlights {
19473                assert_eq!(
19474                    highlight.1.background_color,
19475                    Some(cx.theme().status().created_background)
19476                );
19477            }
19478        },
19479    )
19480    .await;
19481}
19482
19483#[gpui::test]
19484async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
19485    init_test(cx, |_| {});
19486
19487    // Deletion
19488    assert_highlighted_edits(
19489        "Hello, world!",
19490        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
19491        true,
19492        cx,
19493        |highlighted_edits, cx| {
19494            assert_eq!(highlighted_edits.text, "Hello, world!");
19495            assert_eq!(highlighted_edits.highlights.len(), 1);
19496            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
19497            assert_eq!(
19498                highlighted_edits.highlights[0].1.background_color,
19499                Some(cx.theme().status().deleted_background)
19500            );
19501        },
19502    )
19503    .await;
19504
19505    // Insertion
19506    assert_highlighted_edits(
19507        "Hello, world!",
19508        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
19509        true,
19510        cx,
19511        |highlighted_edits, cx| {
19512            assert_eq!(highlighted_edits.highlights.len(), 1);
19513            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
19514            assert_eq!(
19515                highlighted_edits.highlights[0].1.background_color,
19516                Some(cx.theme().status().created_background)
19517            );
19518        },
19519    )
19520    .await;
19521}
19522
19523async fn assert_highlighted_edits(
19524    text: &str,
19525    edits: Vec<(Range<Point>, String)>,
19526    include_deletions: bool,
19527    cx: &mut TestAppContext,
19528    assertion_fn: impl Fn(HighlightedText, &App),
19529) {
19530    let window = cx.add_window(|window, cx| {
19531        let buffer = MultiBuffer::build_simple(text, cx);
19532        Editor::new(EditorMode::full(), buffer, None, window, cx)
19533    });
19534    let cx = &mut VisualTestContext::from_window(*window, cx);
19535
19536    let (buffer, snapshot) = window
19537        .update(cx, |editor, _window, cx| {
19538            (
19539                editor.buffer().clone(),
19540                editor.buffer().read(cx).snapshot(cx),
19541            )
19542        })
19543        .unwrap();
19544
19545    let edits = edits
19546        .into_iter()
19547        .map(|(range, edit)| {
19548            (
19549                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
19550                edit,
19551            )
19552        })
19553        .collect::<Vec<_>>();
19554
19555    let text_anchor_edits = edits
19556        .clone()
19557        .into_iter()
19558        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
19559        .collect::<Vec<_>>();
19560
19561    let edit_preview = window
19562        .update(cx, |_, _window, cx| {
19563            buffer
19564                .read(cx)
19565                .as_singleton()
19566                .unwrap()
19567                .read(cx)
19568                .preview_edits(text_anchor_edits.into(), cx)
19569        })
19570        .unwrap()
19571        .await;
19572
19573    cx.update(|_window, cx| {
19574        let highlighted_edits = inline_completion_edit_text(
19575            &snapshot.as_singleton().unwrap().2,
19576            &edits,
19577            &edit_preview,
19578            include_deletions,
19579            cx,
19580        );
19581        assertion_fn(highlighted_edits, cx)
19582    });
19583}
19584
19585#[track_caller]
19586fn assert_breakpoint(
19587    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
19588    path: &Arc<Path>,
19589    expected: Vec<(u32, Breakpoint)>,
19590) {
19591    if expected.len() == 0usize {
19592        assert!(!breakpoints.contains_key(path), "{}", path.display());
19593    } else {
19594        let mut breakpoint = breakpoints
19595            .get(path)
19596            .unwrap()
19597            .into_iter()
19598            .map(|breakpoint| {
19599                (
19600                    breakpoint.row,
19601                    Breakpoint {
19602                        message: breakpoint.message.clone(),
19603                        state: breakpoint.state,
19604                        condition: breakpoint.condition.clone(),
19605                        hit_condition: breakpoint.hit_condition.clone(),
19606                    },
19607                )
19608            })
19609            .collect::<Vec<_>>();
19610
19611        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
19612
19613        assert_eq!(expected, breakpoint);
19614    }
19615}
19616
19617fn add_log_breakpoint_at_cursor(
19618    editor: &mut Editor,
19619    log_message: &str,
19620    window: &mut Window,
19621    cx: &mut Context<Editor>,
19622) {
19623    let (anchor, bp) = editor
19624        .breakpoints_at_cursors(window, cx)
19625        .first()
19626        .and_then(|(anchor, bp)| {
19627            if let Some(bp) = bp {
19628                Some((*anchor, bp.clone()))
19629            } else {
19630                None
19631            }
19632        })
19633        .unwrap_or_else(|| {
19634            let cursor_position: Point = editor.selections.newest(cx).head();
19635
19636            let breakpoint_position = editor
19637                .snapshot(window, cx)
19638                .display_snapshot
19639                .buffer_snapshot
19640                .anchor_before(Point::new(cursor_position.row, 0));
19641
19642            (breakpoint_position, Breakpoint::new_log(&log_message))
19643        });
19644
19645    editor.edit_breakpoint_at_anchor(
19646        anchor,
19647        bp,
19648        BreakpointEditAction::EditLogMessage(log_message.into()),
19649        cx,
19650    );
19651}
19652
19653#[gpui::test]
19654async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
19655    init_test(cx, |_| {});
19656
19657    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19658    let fs = FakeFs::new(cx.executor());
19659    fs.insert_tree(
19660        path!("/a"),
19661        json!({
19662            "main.rs": sample_text,
19663        }),
19664    )
19665    .await;
19666    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19667    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19668    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19669
19670    let fs = FakeFs::new(cx.executor());
19671    fs.insert_tree(
19672        path!("/a"),
19673        json!({
19674            "main.rs": sample_text,
19675        }),
19676    )
19677    .await;
19678    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19679    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19680    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19681    let worktree_id = workspace
19682        .update(cx, |workspace, _window, cx| {
19683            workspace.project().update(cx, |project, cx| {
19684                project.worktrees(cx).next().unwrap().read(cx).id()
19685            })
19686        })
19687        .unwrap();
19688
19689    let buffer = project
19690        .update(cx, |project, cx| {
19691            project.open_buffer((worktree_id, "main.rs"), cx)
19692        })
19693        .await
19694        .unwrap();
19695
19696    let (editor, cx) = cx.add_window_view(|window, cx| {
19697        Editor::new(
19698            EditorMode::full(),
19699            MultiBuffer::build_from_buffer(buffer, cx),
19700            Some(project.clone()),
19701            window,
19702            cx,
19703        )
19704    });
19705
19706    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19707    let abs_path = project.read_with(cx, |project, cx| {
19708        project
19709            .absolute_path(&project_path, cx)
19710            .map(|path_buf| Arc::from(path_buf.to_owned()))
19711            .unwrap()
19712    });
19713
19714    // assert we can add breakpoint on the first line
19715    editor.update_in(cx, |editor, window, cx| {
19716        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19717        editor.move_to_end(&MoveToEnd, window, cx);
19718        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19719    });
19720
19721    let breakpoints = editor.update(cx, |editor, cx| {
19722        editor
19723            .breakpoint_store()
19724            .as_ref()
19725            .unwrap()
19726            .read(cx)
19727            .all_source_breakpoints(cx)
19728            .clone()
19729    });
19730
19731    assert_eq!(1, breakpoints.len());
19732    assert_breakpoint(
19733        &breakpoints,
19734        &abs_path,
19735        vec![
19736            (0, Breakpoint::new_standard()),
19737            (3, Breakpoint::new_standard()),
19738        ],
19739    );
19740
19741    editor.update_in(cx, |editor, window, cx| {
19742        editor.move_to_beginning(&MoveToBeginning, window, cx);
19743        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19744    });
19745
19746    let breakpoints = editor.update(cx, |editor, cx| {
19747        editor
19748            .breakpoint_store()
19749            .as_ref()
19750            .unwrap()
19751            .read(cx)
19752            .all_source_breakpoints(cx)
19753            .clone()
19754    });
19755
19756    assert_eq!(1, breakpoints.len());
19757    assert_breakpoint(
19758        &breakpoints,
19759        &abs_path,
19760        vec![(3, Breakpoint::new_standard())],
19761    );
19762
19763    editor.update_in(cx, |editor, window, cx| {
19764        editor.move_to_end(&MoveToEnd, window, cx);
19765        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19766    });
19767
19768    let breakpoints = editor.update(cx, |editor, cx| {
19769        editor
19770            .breakpoint_store()
19771            .as_ref()
19772            .unwrap()
19773            .read(cx)
19774            .all_source_breakpoints(cx)
19775            .clone()
19776    });
19777
19778    assert_eq!(0, breakpoints.len());
19779    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19780}
19781
19782#[gpui::test]
19783async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
19784    init_test(cx, |_| {});
19785
19786    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19787
19788    let fs = FakeFs::new(cx.executor());
19789    fs.insert_tree(
19790        path!("/a"),
19791        json!({
19792            "main.rs": sample_text,
19793        }),
19794    )
19795    .await;
19796    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19797    let (workspace, cx) =
19798        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19799
19800    let worktree_id = workspace.update(cx, |workspace, cx| {
19801        workspace.project().update(cx, |project, cx| {
19802            project.worktrees(cx).next().unwrap().read(cx).id()
19803        })
19804    });
19805
19806    let buffer = project
19807        .update(cx, |project, cx| {
19808            project.open_buffer((worktree_id, "main.rs"), cx)
19809        })
19810        .await
19811        .unwrap();
19812
19813    let (editor, cx) = cx.add_window_view(|window, cx| {
19814        Editor::new(
19815            EditorMode::full(),
19816            MultiBuffer::build_from_buffer(buffer, cx),
19817            Some(project.clone()),
19818            window,
19819            cx,
19820        )
19821    });
19822
19823    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19824    let abs_path = project.read_with(cx, |project, cx| {
19825        project
19826            .absolute_path(&project_path, cx)
19827            .map(|path_buf| Arc::from(path_buf.to_owned()))
19828            .unwrap()
19829    });
19830
19831    editor.update_in(cx, |editor, window, cx| {
19832        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19833    });
19834
19835    let breakpoints = editor.update(cx, |editor, cx| {
19836        editor
19837            .breakpoint_store()
19838            .as_ref()
19839            .unwrap()
19840            .read(cx)
19841            .all_source_breakpoints(cx)
19842            .clone()
19843    });
19844
19845    assert_breakpoint(
19846        &breakpoints,
19847        &abs_path,
19848        vec![(0, Breakpoint::new_log("hello world"))],
19849    );
19850
19851    // Removing a log message from a log breakpoint should remove it
19852    editor.update_in(cx, |editor, window, cx| {
19853        add_log_breakpoint_at_cursor(editor, "", window, cx);
19854    });
19855
19856    let breakpoints = editor.update(cx, |editor, cx| {
19857        editor
19858            .breakpoint_store()
19859            .as_ref()
19860            .unwrap()
19861            .read(cx)
19862            .all_source_breakpoints(cx)
19863            .clone()
19864    });
19865
19866    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19867
19868    editor.update_in(cx, |editor, window, cx| {
19869        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19870        editor.move_to_end(&MoveToEnd, window, cx);
19871        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19872        // Not adding a log message to a standard breakpoint shouldn't remove it
19873        add_log_breakpoint_at_cursor(editor, "", window, cx);
19874    });
19875
19876    let breakpoints = editor.update(cx, |editor, cx| {
19877        editor
19878            .breakpoint_store()
19879            .as_ref()
19880            .unwrap()
19881            .read(cx)
19882            .all_source_breakpoints(cx)
19883            .clone()
19884    });
19885
19886    assert_breakpoint(
19887        &breakpoints,
19888        &abs_path,
19889        vec![
19890            (0, Breakpoint::new_standard()),
19891            (3, Breakpoint::new_standard()),
19892        ],
19893    );
19894
19895    editor.update_in(cx, |editor, window, cx| {
19896        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19897    });
19898
19899    let breakpoints = editor.update(cx, |editor, cx| {
19900        editor
19901            .breakpoint_store()
19902            .as_ref()
19903            .unwrap()
19904            .read(cx)
19905            .all_source_breakpoints(cx)
19906            .clone()
19907    });
19908
19909    assert_breakpoint(
19910        &breakpoints,
19911        &abs_path,
19912        vec![
19913            (0, Breakpoint::new_standard()),
19914            (3, Breakpoint::new_log("hello world")),
19915        ],
19916    );
19917
19918    editor.update_in(cx, |editor, window, cx| {
19919        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19920    });
19921
19922    let breakpoints = editor.update(cx, |editor, cx| {
19923        editor
19924            .breakpoint_store()
19925            .as_ref()
19926            .unwrap()
19927            .read(cx)
19928            .all_source_breakpoints(cx)
19929            .clone()
19930    });
19931
19932    assert_breakpoint(
19933        &breakpoints,
19934        &abs_path,
19935        vec![
19936            (0, Breakpoint::new_standard()),
19937            (3, Breakpoint::new_log("hello Earth!!")),
19938        ],
19939    );
19940}
19941
19942/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19943/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19944/// or when breakpoints were placed out of order. This tests for a regression too
19945#[gpui::test]
19946async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19947    init_test(cx, |_| {});
19948
19949    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19950    let fs = FakeFs::new(cx.executor());
19951    fs.insert_tree(
19952        path!("/a"),
19953        json!({
19954            "main.rs": sample_text,
19955        }),
19956    )
19957    .await;
19958    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19959    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19960    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19961
19962    let fs = FakeFs::new(cx.executor());
19963    fs.insert_tree(
19964        path!("/a"),
19965        json!({
19966            "main.rs": sample_text,
19967        }),
19968    )
19969    .await;
19970    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19971    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19972    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19973    let worktree_id = workspace
19974        .update(cx, |workspace, _window, cx| {
19975            workspace.project().update(cx, |project, cx| {
19976                project.worktrees(cx).next().unwrap().read(cx).id()
19977            })
19978        })
19979        .unwrap();
19980
19981    let buffer = project
19982        .update(cx, |project, cx| {
19983            project.open_buffer((worktree_id, "main.rs"), cx)
19984        })
19985        .await
19986        .unwrap();
19987
19988    let (editor, cx) = cx.add_window_view(|window, cx| {
19989        Editor::new(
19990            EditorMode::full(),
19991            MultiBuffer::build_from_buffer(buffer, cx),
19992            Some(project.clone()),
19993            window,
19994            cx,
19995        )
19996    });
19997
19998    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19999    let abs_path = project.read_with(cx, |project, cx| {
20000        project
20001            .absolute_path(&project_path, cx)
20002            .map(|path_buf| Arc::from(path_buf.to_owned()))
20003            .unwrap()
20004    });
20005
20006    // assert we can add breakpoint on the first line
20007    editor.update_in(cx, |editor, window, cx| {
20008        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
20009        editor.move_to_end(&MoveToEnd, window, cx);
20010        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
20011        editor.move_up(&MoveUp, window, cx);
20012        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
20013    });
20014
20015    let breakpoints = editor.update(cx, |editor, cx| {
20016        editor
20017            .breakpoint_store()
20018            .as_ref()
20019            .unwrap()
20020            .read(cx)
20021            .all_source_breakpoints(cx)
20022            .clone()
20023    });
20024
20025    assert_eq!(1, breakpoints.len());
20026    assert_breakpoint(
20027        &breakpoints,
20028        &abs_path,
20029        vec![
20030            (0, Breakpoint::new_standard()),
20031            (2, Breakpoint::new_standard()),
20032            (3, Breakpoint::new_standard()),
20033        ],
20034    );
20035
20036    editor.update_in(cx, |editor, window, cx| {
20037        editor.move_to_beginning(&MoveToBeginning, window, cx);
20038        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
20039        editor.move_to_end(&MoveToEnd, window, cx);
20040        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
20041        // Disabling a breakpoint that doesn't exist should do nothing
20042        editor.move_up(&MoveUp, window, cx);
20043        editor.move_up(&MoveUp, window, cx);
20044        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
20045    });
20046
20047    let breakpoints = editor.update(cx, |editor, cx| {
20048        editor
20049            .breakpoint_store()
20050            .as_ref()
20051            .unwrap()
20052            .read(cx)
20053            .all_source_breakpoints(cx)
20054            .clone()
20055    });
20056
20057    let disable_breakpoint = {
20058        let mut bp = Breakpoint::new_standard();
20059        bp.state = BreakpointState::Disabled;
20060        bp
20061    };
20062
20063    assert_eq!(1, breakpoints.len());
20064    assert_breakpoint(
20065        &breakpoints,
20066        &abs_path,
20067        vec![
20068            (0, disable_breakpoint.clone()),
20069            (2, Breakpoint::new_standard()),
20070            (3, disable_breakpoint.clone()),
20071        ],
20072    );
20073
20074    editor.update_in(cx, |editor, window, cx| {
20075        editor.move_to_beginning(&MoveToBeginning, window, cx);
20076        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
20077        editor.move_to_end(&MoveToEnd, window, cx);
20078        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
20079        editor.move_up(&MoveUp, window, cx);
20080        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
20081    });
20082
20083    let breakpoints = editor.update(cx, |editor, cx| {
20084        editor
20085            .breakpoint_store()
20086            .as_ref()
20087            .unwrap()
20088            .read(cx)
20089            .all_source_breakpoints(cx)
20090            .clone()
20091    });
20092
20093    assert_eq!(1, breakpoints.len());
20094    assert_breakpoint(
20095        &breakpoints,
20096        &abs_path,
20097        vec![
20098            (0, Breakpoint::new_standard()),
20099            (2, disable_breakpoint),
20100            (3, Breakpoint::new_standard()),
20101        ],
20102    );
20103}
20104
20105#[gpui::test]
20106async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
20107    init_test(cx, |_| {});
20108    let capabilities = lsp::ServerCapabilities {
20109        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
20110            prepare_provider: Some(true),
20111            work_done_progress_options: Default::default(),
20112        })),
20113        ..Default::default()
20114    };
20115    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
20116
20117    cx.set_state(indoc! {"
20118        struct Fˇoo {}
20119    "});
20120
20121    cx.update_editor(|editor, _, cx| {
20122        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
20123        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
20124        editor.highlight_background::<DocumentHighlightRead>(
20125            &[highlight_range],
20126            |c| c.editor_document_highlight_read_background,
20127            cx,
20128        );
20129    });
20130
20131    let mut prepare_rename_handler = cx
20132        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
20133            move |_, _, _| async move {
20134                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
20135                    start: lsp::Position {
20136                        line: 0,
20137                        character: 7,
20138                    },
20139                    end: lsp::Position {
20140                        line: 0,
20141                        character: 10,
20142                    },
20143                })))
20144            },
20145        );
20146    let prepare_rename_task = cx
20147        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
20148        .expect("Prepare rename was not started");
20149    prepare_rename_handler.next().await.unwrap();
20150    prepare_rename_task.await.expect("Prepare rename failed");
20151
20152    let mut rename_handler =
20153        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
20154            let edit = lsp::TextEdit {
20155                range: lsp::Range {
20156                    start: lsp::Position {
20157                        line: 0,
20158                        character: 7,
20159                    },
20160                    end: lsp::Position {
20161                        line: 0,
20162                        character: 10,
20163                    },
20164                },
20165                new_text: "FooRenamed".to_string(),
20166            };
20167            Ok(Some(lsp::WorkspaceEdit::new(
20168                // Specify the same edit twice
20169                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
20170            )))
20171        });
20172    let rename_task = cx
20173        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
20174        .expect("Confirm rename was not started");
20175    rename_handler.next().await.unwrap();
20176    rename_task.await.expect("Confirm rename failed");
20177    cx.run_until_parked();
20178
20179    // Despite two edits, only one is actually applied as those are identical
20180    cx.assert_editor_state(indoc! {"
20181        struct FooRenamedˇ {}
20182    "});
20183}
20184
20185#[gpui::test]
20186async fn test_rename_without_prepare(cx: &mut TestAppContext) {
20187    init_test(cx, |_| {});
20188    // These capabilities indicate that the server does not support prepare rename.
20189    let capabilities = lsp::ServerCapabilities {
20190        rename_provider: Some(lsp::OneOf::Left(true)),
20191        ..Default::default()
20192    };
20193    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
20194
20195    cx.set_state(indoc! {"
20196        struct Fˇoo {}
20197    "});
20198
20199    cx.update_editor(|editor, _window, cx| {
20200        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
20201        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
20202        editor.highlight_background::<DocumentHighlightRead>(
20203            &[highlight_range],
20204            |c| c.editor_document_highlight_read_background,
20205            cx,
20206        );
20207    });
20208
20209    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
20210        .expect("Prepare rename was not started")
20211        .await
20212        .expect("Prepare rename failed");
20213
20214    let mut rename_handler =
20215        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
20216            let edit = lsp::TextEdit {
20217                range: lsp::Range {
20218                    start: lsp::Position {
20219                        line: 0,
20220                        character: 7,
20221                    },
20222                    end: lsp::Position {
20223                        line: 0,
20224                        character: 10,
20225                    },
20226                },
20227                new_text: "FooRenamed".to_string(),
20228            };
20229            Ok(Some(lsp::WorkspaceEdit::new(
20230                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
20231            )))
20232        });
20233    let rename_task = cx
20234        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
20235        .expect("Confirm rename was not started");
20236    rename_handler.next().await.unwrap();
20237    rename_task.await.expect("Confirm rename failed");
20238    cx.run_until_parked();
20239
20240    // Correct range is renamed, as `surrounding_word` is used to find it.
20241    cx.assert_editor_state(indoc! {"
20242        struct FooRenamedˇ {}
20243    "});
20244}
20245
20246#[gpui::test]
20247async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
20248    init_test(cx, |_| {});
20249    let mut cx = EditorTestContext::new(cx).await;
20250
20251    let language = Arc::new(
20252        Language::new(
20253            LanguageConfig::default(),
20254            Some(tree_sitter_html::LANGUAGE.into()),
20255        )
20256        .with_brackets_query(
20257            r#"
20258            ("<" @open "/>" @close)
20259            ("</" @open ">" @close)
20260            ("<" @open ">" @close)
20261            ("\"" @open "\"" @close)
20262            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
20263        "#,
20264        )
20265        .unwrap(),
20266    );
20267    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20268
20269    cx.set_state(indoc! {"
20270        <span>ˇ</span>
20271    "});
20272    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
20273    cx.assert_editor_state(indoc! {"
20274        <span>
20275        ˇ
20276        </span>
20277    "});
20278
20279    cx.set_state(indoc! {"
20280        <span><span></span>ˇ</span>
20281    "});
20282    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
20283    cx.assert_editor_state(indoc! {"
20284        <span><span></span>
20285        ˇ</span>
20286    "});
20287
20288    cx.set_state(indoc! {"
20289        <span>ˇ
20290        </span>
20291    "});
20292    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
20293    cx.assert_editor_state(indoc! {"
20294        <span>
20295        ˇ
20296        </span>
20297    "});
20298}
20299
20300#[gpui::test(iterations = 10)]
20301async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
20302    init_test(cx, |_| {});
20303
20304    let fs = FakeFs::new(cx.executor());
20305    fs.insert_tree(
20306        path!("/dir"),
20307        json!({
20308            "a.ts": "a",
20309        }),
20310    )
20311    .await;
20312
20313    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
20314    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20315    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20316
20317    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20318    language_registry.add(Arc::new(Language::new(
20319        LanguageConfig {
20320            name: "TypeScript".into(),
20321            matcher: LanguageMatcher {
20322                path_suffixes: vec!["ts".to_string()],
20323                ..Default::default()
20324            },
20325            ..Default::default()
20326        },
20327        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
20328    )));
20329    let mut fake_language_servers = language_registry.register_fake_lsp(
20330        "TypeScript",
20331        FakeLspAdapter {
20332            capabilities: lsp::ServerCapabilities {
20333                code_lens_provider: Some(lsp::CodeLensOptions {
20334                    resolve_provider: Some(true),
20335                }),
20336                execute_command_provider: Some(lsp::ExecuteCommandOptions {
20337                    commands: vec!["_the/command".to_string()],
20338                    ..lsp::ExecuteCommandOptions::default()
20339                }),
20340                ..lsp::ServerCapabilities::default()
20341            },
20342            ..FakeLspAdapter::default()
20343        },
20344    );
20345
20346    let (buffer, _handle) = project
20347        .update(cx, |p, cx| {
20348            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
20349        })
20350        .await
20351        .unwrap();
20352    cx.executor().run_until_parked();
20353
20354    let fake_server = fake_language_servers.next().await.unwrap();
20355
20356    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
20357    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
20358    drop(buffer_snapshot);
20359    let actions = cx
20360        .update_window(*workspace, |_, window, cx| {
20361            project.code_actions(&buffer, anchor..anchor, window, cx)
20362        })
20363        .unwrap();
20364
20365    fake_server
20366        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
20367            Ok(Some(vec![
20368                lsp::CodeLens {
20369                    range: lsp::Range::default(),
20370                    command: Some(lsp::Command {
20371                        title: "Code lens command".to_owned(),
20372                        command: "_the/command".to_owned(),
20373                        arguments: None,
20374                    }),
20375                    data: None,
20376                },
20377                lsp::CodeLens {
20378                    range: lsp::Range::default(),
20379                    command: Some(lsp::Command {
20380                        title: "Command not in capabilities".to_owned(),
20381                        command: "not in capabilities".to_owned(),
20382                        arguments: None,
20383                    }),
20384                    data: None,
20385                },
20386                lsp::CodeLens {
20387                    range: lsp::Range {
20388                        start: lsp::Position {
20389                            line: 1,
20390                            character: 1,
20391                        },
20392                        end: lsp::Position {
20393                            line: 1,
20394                            character: 1,
20395                        },
20396                    },
20397                    command: Some(lsp::Command {
20398                        title: "Command not in range".to_owned(),
20399                        command: "_the/command".to_owned(),
20400                        arguments: None,
20401                    }),
20402                    data: None,
20403                },
20404            ]))
20405        })
20406        .next()
20407        .await;
20408
20409    let actions = actions.await.unwrap();
20410    assert_eq!(
20411        actions.len(),
20412        1,
20413        "Should have only one valid action for the 0..0 range"
20414    );
20415    let action = actions[0].clone();
20416    let apply = project.update(cx, |project, cx| {
20417        project.apply_code_action(buffer.clone(), action, true, cx)
20418    });
20419
20420    // Resolving the code action does not populate its edits. In absence of
20421    // edits, we must execute the given command.
20422    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
20423        |mut lens, _| async move {
20424            let lens_command = lens.command.as_mut().expect("should have a command");
20425            assert_eq!(lens_command.title, "Code lens command");
20426            lens_command.arguments = Some(vec![json!("the-argument")]);
20427            Ok(lens)
20428        },
20429    );
20430
20431    // While executing the command, the language server sends the editor
20432    // a `workspaceEdit` request.
20433    fake_server
20434        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
20435            let fake = fake_server.clone();
20436            move |params, _| {
20437                assert_eq!(params.command, "_the/command");
20438                let fake = fake.clone();
20439                async move {
20440                    fake.server
20441                        .request::<lsp::request::ApplyWorkspaceEdit>(
20442                            lsp::ApplyWorkspaceEditParams {
20443                                label: None,
20444                                edit: lsp::WorkspaceEdit {
20445                                    changes: Some(
20446                                        [(
20447                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
20448                                            vec![lsp::TextEdit {
20449                                                range: lsp::Range::new(
20450                                                    lsp::Position::new(0, 0),
20451                                                    lsp::Position::new(0, 0),
20452                                                ),
20453                                                new_text: "X".into(),
20454                                            }],
20455                                        )]
20456                                        .into_iter()
20457                                        .collect(),
20458                                    ),
20459                                    ..Default::default()
20460                                },
20461                            },
20462                        )
20463                        .await
20464                        .into_response()
20465                        .unwrap();
20466                    Ok(Some(json!(null)))
20467                }
20468            }
20469        })
20470        .next()
20471        .await;
20472
20473    // Applying the code lens command returns a project transaction containing the edits
20474    // sent by the language server in its `workspaceEdit` request.
20475    let transaction = apply.await.unwrap();
20476    assert!(transaction.0.contains_key(&buffer));
20477    buffer.update(cx, |buffer, cx| {
20478        assert_eq!(buffer.text(), "Xa");
20479        buffer.undo(cx);
20480        assert_eq!(buffer.text(), "a");
20481    });
20482}
20483
20484#[gpui::test]
20485async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
20486    init_test(cx, |_| {});
20487
20488    let fs = FakeFs::new(cx.executor());
20489    let main_text = r#"fn main() {
20490println!("1");
20491println!("2");
20492println!("3");
20493println!("4");
20494println!("5");
20495}"#;
20496    let lib_text = "mod foo {}";
20497    fs.insert_tree(
20498        path!("/a"),
20499        json!({
20500            "lib.rs": lib_text,
20501            "main.rs": main_text,
20502        }),
20503    )
20504    .await;
20505
20506    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20507    let (workspace, cx) =
20508        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20509    let worktree_id = workspace.update(cx, |workspace, cx| {
20510        workspace.project().update(cx, |project, cx| {
20511            project.worktrees(cx).next().unwrap().read(cx).id()
20512        })
20513    });
20514
20515    let expected_ranges = vec![
20516        Point::new(0, 0)..Point::new(0, 0),
20517        Point::new(1, 0)..Point::new(1, 1),
20518        Point::new(2, 0)..Point::new(2, 2),
20519        Point::new(3, 0)..Point::new(3, 3),
20520    ];
20521
20522    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20523    let editor_1 = workspace
20524        .update_in(cx, |workspace, window, cx| {
20525            workspace.open_path(
20526                (worktree_id, "main.rs"),
20527                Some(pane_1.downgrade()),
20528                true,
20529                window,
20530                cx,
20531            )
20532        })
20533        .unwrap()
20534        .await
20535        .downcast::<Editor>()
20536        .unwrap();
20537    pane_1.update(cx, |pane, cx| {
20538        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20539        open_editor.update(cx, |editor, cx| {
20540            assert_eq!(
20541                editor.display_text(cx),
20542                main_text,
20543                "Original main.rs text on initial open",
20544            );
20545            assert_eq!(
20546                editor
20547                    .selections
20548                    .all::<Point>(cx)
20549                    .into_iter()
20550                    .map(|s| s.range())
20551                    .collect::<Vec<_>>(),
20552                vec![Point::zero()..Point::zero()],
20553                "Default selections on initial open",
20554            );
20555        })
20556    });
20557    editor_1.update_in(cx, |editor, window, cx| {
20558        editor.change_selections(None, window, cx, |s| {
20559            s.select_ranges(expected_ranges.clone());
20560        });
20561    });
20562
20563    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
20564        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
20565    });
20566    let editor_2 = workspace
20567        .update_in(cx, |workspace, window, cx| {
20568            workspace.open_path(
20569                (worktree_id, "main.rs"),
20570                Some(pane_2.downgrade()),
20571                true,
20572                window,
20573                cx,
20574            )
20575        })
20576        .unwrap()
20577        .await
20578        .downcast::<Editor>()
20579        .unwrap();
20580    pane_2.update(cx, |pane, cx| {
20581        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20582        open_editor.update(cx, |editor, cx| {
20583            assert_eq!(
20584                editor.display_text(cx),
20585                main_text,
20586                "Original main.rs text on initial open in another panel",
20587            );
20588            assert_eq!(
20589                editor
20590                    .selections
20591                    .all::<Point>(cx)
20592                    .into_iter()
20593                    .map(|s| s.range())
20594                    .collect::<Vec<_>>(),
20595                vec![Point::zero()..Point::zero()],
20596                "Default selections on initial open in another panel",
20597            );
20598        })
20599    });
20600
20601    editor_2.update_in(cx, |editor, window, cx| {
20602        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
20603    });
20604
20605    let _other_editor_1 = workspace
20606        .update_in(cx, |workspace, window, cx| {
20607            workspace.open_path(
20608                (worktree_id, "lib.rs"),
20609                Some(pane_1.downgrade()),
20610                true,
20611                window,
20612                cx,
20613            )
20614        })
20615        .unwrap()
20616        .await
20617        .downcast::<Editor>()
20618        .unwrap();
20619    pane_1
20620        .update_in(cx, |pane, window, cx| {
20621            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
20622        })
20623        .await
20624        .unwrap();
20625    drop(editor_1);
20626    pane_1.update(cx, |pane, cx| {
20627        pane.active_item()
20628            .unwrap()
20629            .downcast::<Editor>()
20630            .unwrap()
20631            .update(cx, |editor, cx| {
20632                assert_eq!(
20633                    editor.display_text(cx),
20634                    lib_text,
20635                    "Other file should be open and active",
20636                );
20637            });
20638        assert_eq!(pane.items().count(), 1, "No other editors should be open");
20639    });
20640
20641    let _other_editor_2 = workspace
20642        .update_in(cx, |workspace, window, cx| {
20643            workspace.open_path(
20644                (worktree_id, "lib.rs"),
20645                Some(pane_2.downgrade()),
20646                true,
20647                window,
20648                cx,
20649            )
20650        })
20651        .unwrap()
20652        .await
20653        .downcast::<Editor>()
20654        .unwrap();
20655    pane_2
20656        .update_in(cx, |pane, window, cx| {
20657            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
20658        })
20659        .await
20660        .unwrap();
20661    drop(editor_2);
20662    pane_2.update(cx, |pane, cx| {
20663        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20664        open_editor.update(cx, |editor, cx| {
20665            assert_eq!(
20666                editor.display_text(cx),
20667                lib_text,
20668                "Other file should be open and active in another panel too",
20669            );
20670        });
20671        assert_eq!(
20672            pane.items().count(),
20673            1,
20674            "No other editors should be open in another pane",
20675        );
20676    });
20677
20678    let _editor_1_reopened = workspace
20679        .update_in(cx, |workspace, window, cx| {
20680            workspace.open_path(
20681                (worktree_id, "main.rs"),
20682                Some(pane_1.downgrade()),
20683                true,
20684                window,
20685                cx,
20686            )
20687        })
20688        .unwrap()
20689        .await
20690        .downcast::<Editor>()
20691        .unwrap();
20692    let _editor_2_reopened = workspace
20693        .update_in(cx, |workspace, window, cx| {
20694            workspace.open_path(
20695                (worktree_id, "main.rs"),
20696                Some(pane_2.downgrade()),
20697                true,
20698                window,
20699                cx,
20700            )
20701        })
20702        .unwrap()
20703        .await
20704        .downcast::<Editor>()
20705        .unwrap();
20706    pane_1.update(cx, |pane, cx| {
20707        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20708        open_editor.update(cx, |editor, cx| {
20709            assert_eq!(
20710                editor.display_text(cx),
20711                main_text,
20712                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
20713            );
20714            assert_eq!(
20715                editor
20716                    .selections
20717                    .all::<Point>(cx)
20718                    .into_iter()
20719                    .map(|s| s.range())
20720                    .collect::<Vec<_>>(),
20721                expected_ranges,
20722                "Previous editor in the 1st panel had selections and should get them restored on reopen",
20723            );
20724        })
20725    });
20726    pane_2.update(cx, |pane, cx| {
20727        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20728        open_editor.update(cx, |editor, cx| {
20729            assert_eq!(
20730                editor.display_text(cx),
20731                r#"fn main() {
20732⋯rintln!("1");
20733⋯intln!("2");
20734⋯ntln!("3");
20735println!("4");
20736println!("5");
20737}"#,
20738                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
20739            );
20740            assert_eq!(
20741                editor
20742                    .selections
20743                    .all::<Point>(cx)
20744                    .into_iter()
20745                    .map(|s| s.range())
20746                    .collect::<Vec<_>>(),
20747                vec![Point::zero()..Point::zero()],
20748                "Previous editor in the 2nd pane had no selections changed hence should restore none",
20749            );
20750        })
20751    });
20752}
20753
20754#[gpui::test]
20755async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
20756    init_test(cx, |_| {});
20757
20758    let fs = FakeFs::new(cx.executor());
20759    let main_text = r#"fn main() {
20760println!("1");
20761println!("2");
20762println!("3");
20763println!("4");
20764println!("5");
20765}"#;
20766    let lib_text = "mod foo {}";
20767    fs.insert_tree(
20768        path!("/a"),
20769        json!({
20770            "lib.rs": lib_text,
20771            "main.rs": main_text,
20772        }),
20773    )
20774    .await;
20775
20776    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20777    let (workspace, cx) =
20778        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20779    let worktree_id = workspace.update(cx, |workspace, cx| {
20780        workspace.project().update(cx, |project, cx| {
20781            project.worktrees(cx).next().unwrap().read(cx).id()
20782        })
20783    });
20784
20785    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20786    let editor = workspace
20787        .update_in(cx, |workspace, window, cx| {
20788            workspace.open_path(
20789                (worktree_id, "main.rs"),
20790                Some(pane.downgrade()),
20791                true,
20792                window,
20793                cx,
20794            )
20795        })
20796        .unwrap()
20797        .await
20798        .downcast::<Editor>()
20799        .unwrap();
20800    pane.update(cx, |pane, cx| {
20801        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20802        open_editor.update(cx, |editor, cx| {
20803            assert_eq!(
20804                editor.display_text(cx),
20805                main_text,
20806                "Original main.rs text on initial open",
20807            );
20808        })
20809    });
20810    editor.update_in(cx, |editor, window, cx| {
20811        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
20812    });
20813
20814    cx.update_global(|store: &mut SettingsStore, cx| {
20815        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20816            s.restore_on_file_reopen = Some(false);
20817        });
20818    });
20819    editor.update_in(cx, |editor, window, cx| {
20820        editor.fold_ranges(
20821            vec![
20822                Point::new(1, 0)..Point::new(1, 1),
20823                Point::new(2, 0)..Point::new(2, 2),
20824                Point::new(3, 0)..Point::new(3, 3),
20825            ],
20826            false,
20827            window,
20828            cx,
20829        );
20830    });
20831    pane.update_in(cx, |pane, window, cx| {
20832        pane.close_all_items(&CloseAllItems::default(), window, cx)
20833    })
20834    .await
20835    .unwrap();
20836    pane.update(cx, |pane, _| {
20837        assert!(pane.active_item().is_none());
20838    });
20839    cx.update_global(|store: &mut SettingsStore, cx| {
20840        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20841            s.restore_on_file_reopen = Some(true);
20842        });
20843    });
20844
20845    let _editor_reopened = workspace
20846        .update_in(cx, |workspace, window, cx| {
20847            workspace.open_path(
20848                (worktree_id, "main.rs"),
20849                Some(pane.downgrade()),
20850                true,
20851                window,
20852                cx,
20853            )
20854        })
20855        .unwrap()
20856        .await
20857        .downcast::<Editor>()
20858        .unwrap();
20859    pane.update(cx, |pane, cx| {
20860        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20861        open_editor.update(cx, |editor, cx| {
20862            assert_eq!(
20863                editor.display_text(cx),
20864                main_text,
20865                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20866            );
20867        })
20868    });
20869}
20870
20871#[gpui::test]
20872async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20873    struct EmptyModalView {
20874        focus_handle: gpui::FocusHandle,
20875    }
20876    impl EventEmitter<DismissEvent> for EmptyModalView {}
20877    impl Render for EmptyModalView {
20878        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20879            div()
20880        }
20881    }
20882    impl Focusable for EmptyModalView {
20883        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20884            self.focus_handle.clone()
20885        }
20886    }
20887    impl workspace::ModalView for EmptyModalView {}
20888    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20889        EmptyModalView {
20890            focus_handle: cx.focus_handle(),
20891        }
20892    }
20893
20894    init_test(cx, |_| {});
20895
20896    let fs = FakeFs::new(cx.executor());
20897    let project = Project::test(fs, [], cx).await;
20898    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20899    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20900    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20901    let editor = cx.new_window_entity(|window, cx| {
20902        Editor::new(
20903            EditorMode::full(),
20904            buffer,
20905            Some(project.clone()),
20906            window,
20907            cx,
20908        )
20909    });
20910    workspace
20911        .update(cx, |workspace, window, cx| {
20912            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20913        })
20914        .unwrap();
20915    editor.update_in(cx, |editor, window, cx| {
20916        editor.open_context_menu(&OpenContextMenu, window, cx);
20917        assert!(editor.mouse_context_menu.is_some());
20918    });
20919    workspace
20920        .update(cx, |workspace, window, cx| {
20921            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20922        })
20923        .unwrap();
20924    cx.read(|cx| {
20925        assert!(editor.read(cx).mouse_context_menu.is_none());
20926    });
20927}
20928
20929#[gpui::test]
20930async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20931    init_test(cx, |_| {});
20932
20933    let fs = FakeFs::new(cx.executor());
20934    fs.insert_file(path!("/file.html"), Default::default())
20935        .await;
20936
20937    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20938
20939    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20940    let html_language = Arc::new(Language::new(
20941        LanguageConfig {
20942            name: "HTML".into(),
20943            matcher: LanguageMatcher {
20944                path_suffixes: vec!["html".to_string()],
20945                ..LanguageMatcher::default()
20946            },
20947            brackets: BracketPairConfig {
20948                pairs: vec![BracketPair {
20949                    start: "<".into(),
20950                    end: ">".into(),
20951                    close: true,
20952                    ..Default::default()
20953                }],
20954                ..Default::default()
20955            },
20956            ..Default::default()
20957        },
20958        Some(tree_sitter_html::LANGUAGE.into()),
20959    ));
20960    language_registry.add(html_language);
20961    let mut fake_servers = language_registry.register_fake_lsp(
20962        "HTML",
20963        FakeLspAdapter {
20964            capabilities: lsp::ServerCapabilities {
20965                completion_provider: Some(lsp::CompletionOptions {
20966                    resolve_provider: Some(true),
20967                    ..Default::default()
20968                }),
20969                ..Default::default()
20970            },
20971            ..Default::default()
20972        },
20973    );
20974
20975    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20976    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20977
20978    let worktree_id = workspace
20979        .update(cx, |workspace, _window, cx| {
20980            workspace.project().update(cx, |project, cx| {
20981                project.worktrees(cx).next().unwrap().read(cx).id()
20982            })
20983        })
20984        .unwrap();
20985    project
20986        .update(cx, |project, cx| {
20987            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20988        })
20989        .await
20990        .unwrap();
20991    let editor = workspace
20992        .update(cx, |workspace, window, cx| {
20993            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20994        })
20995        .unwrap()
20996        .await
20997        .unwrap()
20998        .downcast::<Editor>()
20999        .unwrap();
21000
21001    let fake_server = fake_servers.next().await.unwrap();
21002    editor.update_in(cx, |editor, window, cx| {
21003        editor.set_text("<ad></ad>", window, cx);
21004        editor.change_selections(None, window, cx, |selections| {
21005            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
21006        });
21007        let Some((buffer, _)) = editor
21008            .buffer
21009            .read(cx)
21010            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
21011        else {
21012            panic!("Failed to get buffer for selection position");
21013        };
21014        let buffer = buffer.read(cx);
21015        let buffer_id = buffer.remote_id();
21016        let opening_range =
21017            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
21018        let closing_range =
21019            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
21020        let mut linked_ranges = HashMap::default();
21021        linked_ranges.insert(
21022            buffer_id,
21023            vec![(opening_range.clone(), vec![closing_range.clone()])],
21024        );
21025        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
21026    });
21027    let mut completion_handle =
21028        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
21029            Ok(Some(lsp::CompletionResponse::Array(vec![
21030                lsp::CompletionItem {
21031                    label: "head".to_string(),
21032                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21033                        lsp::InsertReplaceEdit {
21034                            new_text: "head".to_string(),
21035                            insert: lsp::Range::new(
21036                                lsp::Position::new(0, 1),
21037                                lsp::Position::new(0, 3),
21038                            ),
21039                            replace: lsp::Range::new(
21040                                lsp::Position::new(0, 1),
21041                                lsp::Position::new(0, 3),
21042                            ),
21043                        },
21044                    )),
21045                    ..Default::default()
21046                },
21047            ])))
21048        });
21049    editor.update_in(cx, |editor, window, cx| {
21050        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
21051    });
21052    cx.run_until_parked();
21053    completion_handle.next().await.unwrap();
21054    editor.update(cx, |editor, _| {
21055        assert!(
21056            editor.context_menu_visible(),
21057            "Completion menu should be visible"
21058        );
21059    });
21060    editor.update_in(cx, |editor, window, cx| {
21061        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
21062    });
21063    cx.executor().run_until_parked();
21064    editor.update(cx, |editor, cx| {
21065        assert_eq!(editor.text(cx), "<head></head>");
21066    });
21067}
21068
21069#[gpui::test]
21070async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
21071    init_test(cx, |_| {});
21072
21073    let fs = FakeFs::new(cx.executor());
21074    fs.insert_tree(
21075        path!("/root"),
21076        json!({
21077            "a": {
21078                "main.rs": "fn main() {}",
21079            },
21080            "foo": {
21081                "bar": {
21082                    "external_file.rs": "pub mod external {}",
21083                }
21084            }
21085        }),
21086    )
21087    .await;
21088
21089    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
21090    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
21091    language_registry.add(rust_lang());
21092    let _fake_servers = language_registry.register_fake_lsp(
21093        "Rust",
21094        FakeLspAdapter {
21095            ..FakeLspAdapter::default()
21096        },
21097    );
21098    let (workspace, cx) =
21099        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
21100    let worktree_id = workspace.update(cx, |workspace, cx| {
21101        workspace.project().update(cx, |project, cx| {
21102            project.worktrees(cx).next().unwrap().read(cx).id()
21103        })
21104    });
21105
21106    let assert_language_servers_count =
21107        |expected: usize, context: &str, cx: &mut VisualTestContext| {
21108            project.update(cx, |project, cx| {
21109                let current = project
21110                    .lsp_store()
21111                    .read(cx)
21112                    .as_local()
21113                    .unwrap()
21114                    .language_servers
21115                    .len();
21116                assert_eq!(expected, current, "{context}");
21117            });
21118        };
21119
21120    assert_language_servers_count(
21121        0,
21122        "No servers should be running before any file is open",
21123        cx,
21124    );
21125    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
21126    let main_editor = workspace
21127        .update_in(cx, |workspace, window, cx| {
21128            workspace.open_path(
21129                (worktree_id, "main.rs"),
21130                Some(pane.downgrade()),
21131                true,
21132                window,
21133                cx,
21134            )
21135        })
21136        .unwrap()
21137        .await
21138        .downcast::<Editor>()
21139        .unwrap();
21140    pane.update(cx, |pane, cx| {
21141        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
21142        open_editor.update(cx, |editor, cx| {
21143            assert_eq!(
21144                editor.display_text(cx),
21145                "fn main() {}",
21146                "Original main.rs text on initial open",
21147            );
21148        });
21149        assert_eq!(open_editor, main_editor);
21150    });
21151    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
21152
21153    let external_editor = workspace
21154        .update_in(cx, |workspace, window, cx| {
21155            workspace.open_abs_path(
21156                PathBuf::from("/root/foo/bar/external_file.rs"),
21157                OpenOptions::default(),
21158                window,
21159                cx,
21160            )
21161        })
21162        .await
21163        .expect("opening external file")
21164        .downcast::<Editor>()
21165        .expect("downcasted external file's open element to editor");
21166    pane.update(cx, |pane, cx| {
21167        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
21168        open_editor.update(cx, |editor, cx| {
21169            assert_eq!(
21170                editor.display_text(cx),
21171                "pub mod external {}",
21172                "External file is open now",
21173            );
21174        });
21175        assert_eq!(open_editor, external_editor);
21176    });
21177    assert_language_servers_count(
21178        1,
21179        "Second, external, *.rs file should join the existing server",
21180        cx,
21181    );
21182
21183    pane.update_in(cx, |pane, window, cx| {
21184        pane.close_active_item(&CloseActiveItem::default(), window, cx)
21185    })
21186    .await
21187    .unwrap();
21188    pane.update_in(cx, |pane, window, cx| {
21189        pane.navigate_backward(window, cx);
21190    });
21191    cx.run_until_parked();
21192    pane.update(cx, |pane, cx| {
21193        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
21194        open_editor.update(cx, |editor, cx| {
21195            assert_eq!(
21196                editor.display_text(cx),
21197                "pub mod external {}",
21198                "External file is open now",
21199            );
21200        });
21201    });
21202    assert_language_servers_count(
21203        1,
21204        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
21205        cx,
21206    );
21207
21208    cx.update(|_, cx| {
21209        workspace::reload(&workspace::Reload::default(), cx);
21210    });
21211    assert_language_servers_count(
21212        1,
21213        "After reloading the worktree with local and external files opened, only one project should be started",
21214        cx,
21215    );
21216}
21217
21218#[gpui::test]
21219async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
21220    init_test(cx, |_| {});
21221
21222    let mut cx = EditorTestContext::new(cx).await;
21223    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
21224    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
21225
21226    // test cursor move to start of each line on tab
21227    // for `if`, `elif`, `else`, `while`, `with` and `for`
21228    cx.set_state(indoc! {"
21229        def main():
21230        ˇ    for item in items:
21231        ˇ        while item.active:
21232        ˇ            if item.value > 10:
21233        ˇ                continue
21234        ˇ            elif item.value < 0:
21235        ˇ                break
21236        ˇ            else:
21237        ˇ                with item.context() as ctx:
21238        ˇ                    yield count
21239        ˇ        else:
21240        ˇ            log('while else')
21241        ˇ    else:
21242        ˇ        log('for else')
21243    "});
21244    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
21245    cx.assert_editor_state(indoc! {"
21246        def main():
21247            ˇfor item in items:
21248                ˇwhile item.active:
21249                    ˇif item.value > 10:
21250                        ˇcontinue
21251                    ˇelif item.value < 0:
21252                        ˇbreak
21253                    ˇelse:
21254                        ˇwith item.context() as ctx:
21255                            ˇyield count
21256                ˇelse:
21257                    ˇlog('while else')
21258            ˇelse:
21259                ˇlog('for else')
21260    "});
21261    // test relative indent is preserved when tab
21262    // for `if`, `elif`, `else`, `while`, `with` and `for`
21263    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
21264    cx.assert_editor_state(indoc! {"
21265        def main():
21266                ˇfor item in items:
21267                    ˇwhile item.active:
21268                        ˇif item.value > 10:
21269                            ˇcontinue
21270                        ˇelif item.value < 0:
21271                            ˇbreak
21272                        ˇelse:
21273                            ˇwith item.context() as ctx:
21274                                ˇyield count
21275                    ˇelse:
21276                        ˇlog('while else')
21277                ˇelse:
21278                    ˇlog('for else')
21279    "});
21280
21281    // test cursor move to start of each line on tab
21282    // for `try`, `except`, `else`, `finally`, `match` and `def`
21283    cx.set_state(indoc! {"
21284        def main():
21285        ˇ    try:
21286        ˇ       fetch()
21287        ˇ    except ValueError:
21288        ˇ       handle_error()
21289        ˇ    else:
21290        ˇ        match value:
21291        ˇ            case _:
21292        ˇ    finally:
21293        ˇ        def status():
21294        ˇ            return 0
21295    "});
21296    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
21297    cx.assert_editor_state(indoc! {"
21298        def main():
21299            ˇtry:
21300                ˇfetch()
21301            ˇexcept ValueError:
21302                ˇhandle_error()
21303            ˇelse:
21304                ˇmatch value:
21305                    ˇcase _:
21306            ˇfinally:
21307                ˇdef status():
21308                    ˇreturn 0
21309    "});
21310    // test relative indent is preserved when tab
21311    // for `try`, `except`, `else`, `finally`, `match` and `def`
21312    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
21313    cx.assert_editor_state(indoc! {"
21314        def main():
21315                ˇtry:
21316                    ˇfetch()
21317                ˇexcept ValueError:
21318                    ˇhandle_error()
21319                ˇelse:
21320                    ˇmatch value:
21321                        ˇcase _:
21322                ˇfinally:
21323                    ˇdef status():
21324                        ˇreturn 0
21325    "});
21326}
21327
21328#[gpui::test]
21329async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
21330    init_test(cx, |_| {});
21331
21332    let mut cx = EditorTestContext::new(cx).await;
21333    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
21334    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
21335
21336    // test `else` auto outdents when typed inside `if` block
21337    cx.set_state(indoc! {"
21338        def main():
21339            if i == 2:
21340                return
21341                ˇ
21342    "});
21343    cx.update_editor(|editor, window, cx| {
21344        editor.handle_input("else:", window, cx);
21345    });
21346    cx.assert_editor_state(indoc! {"
21347        def main():
21348            if i == 2:
21349                return
21350            else:ˇ
21351    "});
21352
21353    // test `except` auto outdents when typed inside `try` block
21354    cx.set_state(indoc! {"
21355        def main():
21356            try:
21357                i = 2
21358                ˇ
21359    "});
21360    cx.update_editor(|editor, window, cx| {
21361        editor.handle_input("except:", window, cx);
21362    });
21363    cx.assert_editor_state(indoc! {"
21364        def main():
21365            try:
21366                i = 2
21367            except:ˇ
21368    "});
21369
21370    // test `else` auto outdents when typed inside `except` block
21371    cx.set_state(indoc! {"
21372        def main():
21373            try:
21374                i = 2
21375            except:
21376                j = 2
21377                ˇ
21378    "});
21379    cx.update_editor(|editor, window, cx| {
21380        editor.handle_input("else:", window, cx);
21381    });
21382    cx.assert_editor_state(indoc! {"
21383        def main():
21384            try:
21385                i = 2
21386            except:
21387                j = 2
21388            else:ˇ
21389    "});
21390
21391    // test `finally` auto outdents when typed inside `else` block
21392    cx.set_state(indoc! {"
21393        def main():
21394            try:
21395                i = 2
21396            except:
21397                j = 2
21398            else:
21399                k = 2
21400                ˇ
21401    "});
21402    cx.update_editor(|editor, window, cx| {
21403        editor.handle_input("finally:", window, cx);
21404    });
21405    cx.assert_editor_state(indoc! {"
21406        def main():
21407            try:
21408                i = 2
21409            except:
21410                j = 2
21411            else:
21412                k = 2
21413            finally:ˇ
21414    "});
21415
21416    // TODO: test `except` auto outdents when typed inside `try` block right after for block
21417    // cx.set_state(indoc! {"
21418    //     def main():
21419    //         try:
21420    //             for i in range(n):
21421    //                 pass
21422    //             ˇ
21423    // "});
21424    // cx.update_editor(|editor, window, cx| {
21425    //     editor.handle_input("except:", window, cx);
21426    // });
21427    // cx.assert_editor_state(indoc! {"
21428    //     def main():
21429    //         try:
21430    //             for i in range(n):
21431    //                 pass
21432    //         except:ˇ
21433    // "});
21434
21435    // TODO: test `else` auto outdents when typed inside `except` block right after for block
21436    // cx.set_state(indoc! {"
21437    //     def main():
21438    //         try:
21439    //             i = 2
21440    //         except:
21441    //             for i in range(n):
21442    //                 pass
21443    //             ˇ
21444    // "});
21445    // cx.update_editor(|editor, window, cx| {
21446    //     editor.handle_input("else:", window, cx);
21447    // });
21448    // cx.assert_editor_state(indoc! {"
21449    //     def main():
21450    //         try:
21451    //             i = 2
21452    //         except:
21453    //             for i in range(n):
21454    //                 pass
21455    //         else:ˇ
21456    // "});
21457
21458    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
21459    // cx.set_state(indoc! {"
21460    //     def main():
21461    //         try:
21462    //             i = 2
21463    //         except:
21464    //             j = 2
21465    //         else:
21466    //             for i in range(n):
21467    //                 pass
21468    //             ˇ
21469    // "});
21470    // cx.update_editor(|editor, window, cx| {
21471    //     editor.handle_input("finally:", window, cx);
21472    // });
21473    // cx.assert_editor_state(indoc! {"
21474    //     def main():
21475    //         try:
21476    //             i = 2
21477    //         except:
21478    //             j = 2
21479    //         else:
21480    //             for i in range(n):
21481    //                 pass
21482    //         finally:ˇ
21483    // "});
21484
21485    // test `else` stays at correct indent when typed after `for` block
21486    cx.set_state(indoc! {"
21487        def main():
21488            for i in range(10):
21489                if i == 3:
21490                    break
21491            ˇ
21492    "});
21493    cx.update_editor(|editor, window, cx| {
21494        editor.handle_input("else:", window, cx);
21495    });
21496    cx.assert_editor_state(indoc! {"
21497        def main():
21498            for i in range(10):
21499                if i == 3:
21500                    break
21501            else:ˇ
21502    "});
21503
21504    // test does not outdent on typing after line with square brackets
21505    cx.set_state(indoc! {"
21506        def f() -> list[str]:
21507            ˇ
21508    "});
21509    cx.update_editor(|editor, window, cx| {
21510        editor.handle_input("a", window, cx);
21511    });
21512    cx.assert_editor_state(indoc! {"
21513        def f() -> list[str]:
2151421515    "});
21516}
21517
21518#[gpui::test]
21519async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
21520    init_test(cx, |_| {});
21521    update_test_language_settings(cx, |settings| {
21522        settings.defaults.extend_comment_on_newline = Some(false);
21523    });
21524    let mut cx = EditorTestContext::new(cx).await;
21525    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
21526    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
21527
21528    // test correct indent after newline on comment
21529    cx.set_state(indoc! {"
21530        # COMMENT:ˇ
21531    "});
21532    cx.update_editor(|editor, window, cx| {
21533        editor.newline(&Newline, window, cx);
21534    });
21535    cx.assert_editor_state(indoc! {"
21536        # COMMENT:
21537        ˇ
21538    "});
21539
21540    // test correct indent after newline in brackets
21541    cx.set_state(indoc! {"
21542        {ˇ}
21543    "});
21544    cx.update_editor(|editor, window, cx| {
21545        editor.newline(&Newline, window, cx);
21546    });
21547    cx.run_until_parked();
21548    cx.assert_editor_state(indoc! {"
21549        {
21550            ˇ
21551        }
21552    "});
21553
21554    cx.set_state(indoc! {"
21555        (ˇ)
21556    "});
21557    cx.update_editor(|editor, window, cx| {
21558        editor.newline(&Newline, window, cx);
21559    });
21560    cx.run_until_parked();
21561    cx.assert_editor_state(indoc! {"
21562        (
21563            ˇ
21564        )
21565    "});
21566
21567    // do not indent after empty lists or dictionaries
21568    cx.set_state(indoc! {"
21569        a = []ˇ
21570    "});
21571    cx.update_editor(|editor, window, cx| {
21572        editor.newline(&Newline, window, cx);
21573    });
21574    cx.run_until_parked();
21575    cx.assert_editor_state(indoc! {"
21576        a = []
21577        ˇ
21578    "});
21579}
21580
21581fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
21582    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
21583    point..point
21584}
21585
21586#[track_caller]
21587fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
21588    let (text, ranges) = marked_text_ranges(marked_text, true);
21589    assert_eq!(editor.text(cx), text);
21590    assert_eq!(
21591        editor.selections.ranges(cx),
21592        ranges,
21593        "Assert selections are {}",
21594        marked_text
21595    );
21596}
21597
21598pub fn handle_signature_help_request(
21599    cx: &mut EditorLspTestContext,
21600    mocked_response: lsp::SignatureHelp,
21601) -> impl Future<Output = ()> + use<> {
21602    let mut request =
21603        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
21604            let mocked_response = mocked_response.clone();
21605            async move { Ok(Some(mocked_response)) }
21606        });
21607
21608    async move {
21609        request.next().await;
21610    }
21611}
21612
21613#[track_caller]
21614pub fn check_displayed_completions(expected: Vec<&'static str>, cx: &mut EditorLspTestContext) {
21615    cx.update_editor(|editor, _, _| {
21616        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow().as_ref() {
21617            let entries = menu.entries.borrow();
21618            let entries = entries
21619                .iter()
21620                .map(|entry| entry.string.as_str())
21621                .collect::<Vec<_>>();
21622            assert_eq!(entries, expected);
21623        } else {
21624            panic!("Expected completions menu");
21625        }
21626    });
21627}
21628
21629/// Handle completion request passing a marked string specifying where the completion
21630/// should be triggered from using '|' character, what range should be replaced, and what completions
21631/// should be returned using '<' and '>' to delimit the range.
21632///
21633/// Also see `handle_completion_request_with_insert_and_replace`.
21634#[track_caller]
21635pub fn handle_completion_request(
21636    marked_string: &str,
21637    completions: Vec<&'static str>,
21638    is_incomplete: bool,
21639    counter: Arc<AtomicUsize>,
21640    cx: &mut EditorLspTestContext,
21641) -> impl Future<Output = ()> {
21642    let complete_from_marker: TextRangeMarker = '|'.into();
21643    let replace_range_marker: TextRangeMarker = ('<', '>').into();
21644    let (_, mut marked_ranges) = marked_text_ranges_by(
21645        marked_string,
21646        vec![complete_from_marker.clone(), replace_range_marker.clone()],
21647    );
21648
21649    let complete_from_position =
21650        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
21651    let replace_range =
21652        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
21653
21654    let mut request =
21655        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
21656            let completions = completions.clone();
21657            counter.fetch_add(1, atomic::Ordering::Release);
21658            async move {
21659                assert_eq!(params.text_document_position.text_document.uri, url.clone());
21660                assert_eq!(
21661                    params.text_document_position.position,
21662                    complete_from_position
21663                );
21664                Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
21665                    is_incomplete: is_incomplete,
21666                    item_defaults: None,
21667                    items: completions
21668                        .iter()
21669                        .map(|completion_text| lsp::CompletionItem {
21670                            label: completion_text.to_string(),
21671                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
21672                                range: replace_range,
21673                                new_text: completion_text.to_string(),
21674                            })),
21675                            ..Default::default()
21676                        })
21677                        .collect(),
21678                })))
21679            }
21680        });
21681
21682    async move {
21683        request.next().await;
21684    }
21685}
21686
21687/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
21688/// given instead, which also contains an `insert` range.
21689///
21690/// This function uses markers to define ranges:
21691/// - `|` marks the cursor position
21692/// - `<>` marks the replace range
21693/// - `[]` marks the insert range (optional, defaults to `replace_range.start..cursor_pos`which is what Rust-Analyzer provides)
21694pub fn handle_completion_request_with_insert_and_replace(
21695    cx: &mut EditorLspTestContext,
21696    marked_string: &str,
21697    completions: Vec<(&'static str, &'static str)>, // (label, new_text)
21698    counter: Arc<AtomicUsize>,
21699) -> impl Future<Output = ()> {
21700    let complete_from_marker: TextRangeMarker = '|'.into();
21701    let replace_range_marker: TextRangeMarker = ('<', '>').into();
21702    let insert_range_marker: TextRangeMarker = ('{', '}').into();
21703
21704    let (_, mut marked_ranges) = marked_text_ranges_by(
21705        marked_string,
21706        vec![
21707            complete_from_marker.clone(),
21708            replace_range_marker.clone(),
21709            insert_range_marker.clone(),
21710        ],
21711    );
21712
21713    let complete_from_position =
21714        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
21715    let replace_range =
21716        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
21717
21718    let insert_range = match marked_ranges.remove(&insert_range_marker) {
21719        Some(ranges) if !ranges.is_empty() => cx.to_lsp_range(ranges[0].clone()),
21720        _ => lsp::Range {
21721            start: replace_range.start,
21722            end: complete_from_position,
21723        },
21724    };
21725
21726    let mut request =
21727        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
21728            let completions = completions.clone();
21729            counter.fetch_add(1, atomic::Ordering::Release);
21730            async move {
21731                assert_eq!(params.text_document_position.text_document.uri, url.clone());
21732                assert_eq!(
21733                    params.text_document_position.position, complete_from_position,
21734                    "marker `|` position doesn't match",
21735                );
21736                Ok(Some(lsp::CompletionResponse::Array(
21737                    completions
21738                        .iter()
21739                        .map(|(label, new_text)| lsp::CompletionItem {
21740                            label: label.to_string(),
21741                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21742                                lsp::InsertReplaceEdit {
21743                                    insert: insert_range,
21744                                    replace: replace_range,
21745                                    new_text: new_text.to_string(),
21746                                },
21747                            )),
21748                            ..Default::default()
21749                        })
21750                        .collect(),
21751                )))
21752            }
21753        });
21754
21755    async move {
21756        request.next().await;
21757    }
21758}
21759
21760fn handle_resolve_completion_request(
21761    cx: &mut EditorLspTestContext,
21762    edits: Option<Vec<(&'static str, &'static str)>>,
21763) -> impl Future<Output = ()> {
21764    let edits = edits.map(|edits| {
21765        edits
21766            .iter()
21767            .map(|(marked_string, new_text)| {
21768                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
21769                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
21770                lsp::TextEdit::new(replace_range, new_text.to_string())
21771            })
21772            .collect::<Vec<_>>()
21773    });
21774
21775    let mut request =
21776        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
21777            let edits = edits.clone();
21778            async move {
21779                Ok(lsp::CompletionItem {
21780                    additional_text_edits: edits,
21781                    ..Default::default()
21782                })
21783            }
21784        });
21785
21786    async move {
21787        request.next().await;
21788    }
21789}
21790
21791pub(crate) fn update_test_language_settings(
21792    cx: &mut TestAppContext,
21793    f: impl Fn(&mut AllLanguageSettingsContent),
21794) {
21795    cx.update(|cx| {
21796        SettingsStore::update_global(cx, |store, cx| {
21797            store.update_user_settings::<AllLanguageSettings>(cx, f);
21798        });
21799    });
21800}
21801
21802pub(crate) fn update_test_project_settings(
21803    cx: &mut TestAppContext,
21804    f: impl Fn(&mut ProjectSettings),
21805) {
21806    cx.update(|cx| {
21807        SettingsStore::update_global(cx, |store, cx| {
21808            store.update_user_settings::<ProjectSettings>(cx, f);
21809        });
21810    });
21811}
21812
21813pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
21814    cx.update(|cx| {
21815        assets::Assets.load_test_fonts(cx);
21816        let store = SettingsStore::test(cx);
21817        cx.set_global(store);
21818        theme::init(theme::LoadThemes::JustBase, cx);
21819        release_channel::init(SemanticVersion::default(), cx);
21820        client::init_settings(cx);
21821        language::init(cx);
21822        Project::init_settings(cx);
21823        workspace::init_settings(cx);
21824        crate::init(cx);
21825    });
21826
21827    update_test_language_settings(cx, f);
21828}
21829
21830#[track_caller]
21831fn assert_hunk_revert(
21832    not_reverted_text_with_selections: &str,
21833    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
21834    expected_reverted_text_with_selections: &str,
21835    base_text: &str,
21836    cx: &mut EditorLspTestContext,
21837) {
21838    cx.set_state(not_reverted_text_with_selections);
21839    cx.set_head_text(base_text);
21840    cx.executor().run_until_parked();
21841
21842    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
21843        let snapshot = editor.snapshot(window, cx);
21844        let reverted_hunk_statuses = snapshot
21845            .buffer_snapshot
21846            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
21847            .map(|hunk| hunk.status().kind)
21848            .collect::<Vec<_>>();
21849
21850        editor.git_restore(&Default::default(), window, cx);
21851        reverted_hunk_statuses
21852    });
21853    cx.executor().run_until_parked();
21854    cx.assert_editor_state(expected_reverted_text_with_selections);
21855    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
21856}
21857
21858#[gpui::test(iterations = 10)]
21859async fn test_pulling_diagnostics(cx: &mut TestAppContext) {
21860    init_test(cx, |_| {});
21861
21862    let diagnostic_requests = Arc::new(AtomicUsize::new(0));
21863    let counter = diagnostic_requests.clone();
21864
21865    let fs = FakeFs::new(cx.executor());
21866    fs.insert_tree(
21867        path!("/a"),
21868        json!({
21869            "first.rs": "fn main() { let a = 5; }",
21870            "second.rs": "// Test file",
21871        }),
21872    )
21873    .await;
21874
21875    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
21876    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
21877    let cx = &mut VisualTestContext::from_window(*workspace, cx);
21878
21879    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
21880    language_registry.add(rust_lang());
21881    let mut fake_servers = language_registry.register_fake_lsp(
21882        "Rust",
21883        FakeLspAdapter {
21884            capabilities: lsp::ServerCapabilities {
21885                diagnostic_provider: Some(lsp::DiagnosticServerCapabilities::Options(
21886                    lsp::DiagnosticOptions {
21887                        identifier: None,
21888                        inter_file_dependencies: true,
21889                        workspace_diagnostics: true,
21890                        work_done_progress_options: Default::default(),
21891                    },
21892                )),
21893                ..Default::default()
21894            },
21895            ..Default::default()
21896        },
21897    );
21898
21899    let editor = workspace
21900        .update(cx, |workspace, window, cx| {
21901            workspace.open_abs_path(
21902                PathBuf::from(path!("/a/first.rs")),
21903                OpenOptions::default(),
21904                window,
21905                cx,
21906            )
21907        })
21908        .unwrap()
21909        .await
21910        .unwrap()
21911        .downcast::<Editor>()
21912        .unwrap();
21913    let fake_server = fake_servers.next().await.unwrap();
21914    let mut first_request = fake_server
21915        .set_request_handler::<lsp::request::DocumentDiagnosticRequest, _, _>(move |params, _| {
21916            let new_result_id = counter.fetch_add(1, atomic::Ordering::Release) + 1;
21917            let result_id = Some(new_result_id.to_string());
21918            assert_eq!(
21919                params.text_document.uri,
21920                lsp::Url::from_file_path(path!("/a/first.rs")).unwrap()
21921            );
21922            async move {
21923                Ok(lsp::DocumentDiagnosticReportResult::Report(
21924                    lsp::DocumentDiagnosticReport::Full(lsp::RelatedFullDocumentDiagnosticReport {
21925                        related_documents: None,
21926                        full_document_diagnostic_report: lsp::FullDocumentDiagnosticReport {
21927                            items: Vec::new(),
21928                            result_id,
21929                        },
21930                    }),
21931                ))
21932            }
21933        });
21934
21935    let ensure_result_id = |expected: Option<String>, cx: &mut TestAppContext| {
21936        project.update(cx, |project, cx| {
21937            let buffer_id = editor
21938                .read(cx)
21939                .buffer()
21940                .read(cx)
21941                .as_singleton()
21942                .expect("created a singleton buffer")
21943                .read(cx)
21944                .remote_id();
21945            let buffer_result_id = project.lsp_store().read(cx).result_id(buffer_id);
21946            assert_eq!(expected, buffer_result_id);
21947        });
21948    };
21949
21950    ensure_result_id(None, cx);
21951    cx.executor().advance_clock(Duration::from_millis(60));
21952    cx.executor().run_until_parked();
21953    assert_eq!(
21954        diagnostic_requests.load(atomic::Ordering::Acquire),
21955        1,
21956        "Opening file should trigger diagnostic request"
21957    );
21958    first_request
21959        .next()
21960        .await
21961        .expect("should have sent the first diagnostics pull request");
21962    ensure_result_id(Some("1".to_string()), cx);
21963
21964    // Editing should trigger diagnostics
21965    editor.update_in(cx, |editor, window, cx| {
21966        editor.handle_input("2", window, cx)
21967    });
21968    cx.executor().advance_clock(Duration::from_millis(60));
21969    cx.executor().run_until_parked();
21970    assert_eq!(
21971        diagnostic_requests.load(atomic::Ordering::Acquire),
21972        2,
21973        "Editing should trigger diagnostic request"
21974    );
21975    ensure_result_id(Some("2".to_string()), cx);
21976
21977    // Moving cursor should not trigger diagnostic request
21978    editor.update_in(cx, |editor, window, cx| {
21979        editor.change_selections(None, window, cx, |s| {
21980            s.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
21981        });
21982    });
21983    cx.executor().advance_clock(Duration::from_millis(60));
21984    cx.executor().run_until_parked();
21985    assert_eq!(
21986        diagnostic_requests.load(atomic::Ordering::Acquire),
21987        2,
21988        "Cursor movement should not trigger diagnostic request"
21989    );
21990    ensure_result_id(Some("2".to_string()), cx);
21991
21992    // Multiple rapid edits should be debounced
21993    for _ in 0..5 {
21994        editor.update_in(cx, |editor, window, cx| {
21995            editor.handle_input("x", window, cx)
21996        });
21997    }
21998    cx.executor().advance_clock(Duration::from_millis(60));
21999    cx.executor().run_until_parked();
22000
22001    let final_requests = diagnostic_requests.load(atomic::Ordering::Acquire);
22002    assert!(
22003        final_requests <= 4,
22004        "Multiple rapid edits should be debounced (got {final_requests} requests)",
22005    );
22006    ensure_result_id(Some(final_requests.to_string()), cx);
22007}
22008
22009#[gpui::test]
22010async fn test_add_selection_after_moving_with_multiple_cursors(cx: &mut TestAppContext) {
22011    // Regression test for issue #11671
22012    // Previously, adding a cursor after moving multiple cursors would reset
22013    // the cursor count instead of adding to the existing cursors.
22014    init_test(cx, |_| {});
22015    let mut cx = EditorTestContext::new(cx).await;
22016
22017    // Create a simple buffer with cursor at start
22018    cx.set_state(indoc! {"
22019        ˇaaaa
22020        bbbb
22021        cccc
22022        dddd
22023        eeee
22024        ffff
22025        gggg
22026        hhhh"});
22027
22028    // Add 2 cursors below (so we have 3 total)
22029    cx.update_editor(|editor, window, cx| {
22030        editor.add_selection_below(&Default::default(), window, cx);
22031        editor.add_selection_below(&Default::default(), window, cx);
22032    });
22033
22034    // Verify we have 3 cursors
22035    let initial_count = cx.update_editor(|editor, _, _| editor.selections.count());
22036    assert_eq!(
22037        initial_count, 3,
22038        "Should have 3 cursors after adding 2 below"
22039    );
22040
22041    // Move down one line
22042    cx.update_editor(|editor, window, cx| {
22043        editor.move_down(&MoveDown, window, cx);
22044    });
22045
22046    // Add another cursor below
22047    cx.update_editor(|editor, window, cx| {
22048        editor.add_selection_below(&Default::default(), window, cx);
22049    });
22050
22051    // Should now have 4 cursors (3 original + 1 new)
22052    let final_count = cx.update_editor(|editor, _, _| editor.selections.count());
22053    assert_eq!(
22054        final_count, 4,
22055        "Should have 4 cursors after moving and adding another"
22056    );
22057}